ویژگی تصویر

تابع stream_socket_recvfrom() در PHP

  /  PHP   /  تابع stream_socket_recvfrom() در PHP
بنر تبلیغاتی الف
آموزش PHP

تابع stream_socket_recvfrom() یکی از توابع مفید و کم‌استفاده در PHP برای دریافت داده از یک استریم شبکه (TCP/UDP یا دیگر پروتکل‌های مبتنی بر استریم) است. این تابع به‌ویژه وقتی با سرورهای UDP یا ارتباطات دیتاگرامی کار می‌کنید و نیاز به دانستن آدرس فرستنده دارید، کاربردی است. در این مقاله به صورت دقیق پارامترها، مقدار بازگشتی، نمونه‌های عملی و نکات پیشرفته را بررسی می‌کنیم.

معرفی و امضاء تابع

امضاء کلی تابع به صورت زیر است:

string|false stream_socket_recvfrom ( resource $socket , int $length [, int $flags = 0 [, string &$remote_address ]] )

توضیحات کوتاه:

  • $socket: یک resource که معمولاً از stream_socket_client یا stream_socket_server به دست می‌آید.
  • $length: حداکثر تعداد بایت‌هایی که می‌خواهیم دریافت کنیم.
  • $flags: پرچم‌های اختیاری که رفتار دریافت را تغییر می‌دهند (مثلاً مشابه MSG_PEEK یا MSG_OOB در لایه سوکت؛ پیاده‌سازی و پشتیبانی بسته به سیستم عامل متفاوت است).
  • $remote_address: پارامتر ارسالی-مرجعی که در صورت وجود آدرس فرستنده را (مثلاً “127.0.0.1:12345”) برمی‌گرداند.

مقدار بازگشتی و رفتارهای مهم

  • در موفقیت، یک رشته حاوی داده‌های دریافت‌شده بازگردانده می‌شود.
  • در صورت خطا، مقدار false برگشت داده می‌شود.
  • برای ارتباطات TCP اگر اتصال توسط طرف مقابل بسته شده باشد، تابع رشتهٔ خالی (“”) بازمی‌گرداند که باید به معنی EOF تفسیر شود.
  • برای پروتکل‌های دیتاگرامی مثل UDP، $remote_address معمولاً حاوی آدرس فرستنده است.
  • اگر طول داده از $length بیشتر باشد، با توجه به سیستم عامل، داده‌ها ممکن است تا آن طول بریده شوند و بقیه دور ریخته شود.

نمونه عملی: سرور UDP ساده

<?php
$addr = "udp://0.0.0.0:12345";
$socket = stream_socket_server($addr, $errno, $errstr, STREAM_SERVER_BIND);

if (!$socket) {
    die("Failed to bind: $errstr ($errno)n");
}

while (true) {
    $data = stream_socket_recvfrom($socket, 1500, 0, $peer);
    if ($data === false) {
        // خطای دریافت
        continue;
    }
    echo "Received from $peer: $datan";
    // پاسخ به همان آدرس با stream_socket_sendto
    stream_socket_sendto($socket, "ACK", 0, $peer);
}
?>

توضیح: این کد یک سرور UDP ساده روی پورت 12345 ایجاد می‌کند. در حلقهٔ بی‌نهایت، با stream_socket_recvfrom داده را دریافت کرده و آدرس فرستنده را در متغیر $peer قرار می‌دهد. سپس با stream_socket_sendto پاسخی به همان آدرس می‌فرستد. توجه داشته باشید طول 1500 بایت انتخاب شد که معادل MTU تقریبی است تا از برش جلوگیری کنیم.

نمونه: کار با TCP و تشخیص EOF

<?php
$server = stream_socket_server("tcp://0.0.0.0:23456", $errno, $errstr);
if (!$server) die("Error: $errstrn");

$client = stream_socket_accept($server);
if ($client) {
    while (!feof($client)) {
        $data = stream_socket_recvfrom($client, 4096);
        if ($data === false) break;
        if ($data === "") {
            // اتصال توسط کلاینت بسته شده
            break;
        }
        echo "Got: $datan";
    }
    fclose($client);
}
fclose($server);
?>

توضیح: در ارتباط TCP، تابع ممکن است رشتهٔ خالی برگرداند که نشان‌دهندهٔ بسته شدن اتصال از سمت مقابل است. بنابراین باید به صراحت این حالت را تشخیص داده و اتصال را ببندید.

پرچم‌ها، غیرمسدود (non-blocking) و زمان‌بندی

  • برای کار در حالت غیرمسدود از stream_set_blocking($socket, false) استفاده کنید. در این حالت stream_socket_recvfrom ممکن است بلافاصله رشتهٔ خالی یا false بازگرداند اگر داده‌ای آماده نباشد.
  • برای انتظار قابل‌کنترل از stream_select برای بررسی readability استفاده نمایید تا از مصرف CPU بالا جلوگیری شود.
  • برای تعیین تایم‌اوت سطحی می‌توانید stream_set_timeout یا stream_context_set_option استفاده کنید.

مقایسه خلاصه با توابع مشابه

تابعموارد استفادهنکته
stream_socket_recvfrom()دریافت از استریم‌ها، مناسب برای UDP که نیاز به آدرس فرستنده باشدآدرس فرستنده را می‌تواند برگرداند
socket_recvfrom()API سوکت سطح پایین با کنترل بیشترنیاز به ایجاد سوکت با socket_create دارد
fread()خواندن ساده از استریم‌های بایتی، معمولاً برای فایل یا TCPآدرس فرستنده را برنمی‌گرداند

نکات حرفه‌ای و تله‌های رایج

  • برای UDP همیشه به احتمال برش (truncation) توجه کنید؛ اگر انتظار پیام‌های بزرگی دارید، طول مناسب تعیین کنید یا از پروتکل لایهٔ اپلیکیشن برای بخش‌بندی استفاده کنید.
  • در حالت non-blocking از stream_select یا حلقه‌های بازجویی همراه با usleep استفاده کنید تا مصرف CPU کاهش یابد.
  • در صورتی که پرچم‌هایی مانند MSG_PEEK استفاده می‌کنید، مطمئن شوید سیستم عامل و نسخهٔ PHP شما آن‌ها را پشتیبانی می‌کند؛ رفتار در سیستم‌عامل‌های مختلف تفاوت دارد.
  • برای برنامه‌های با بار زیاد یا چند کانکشن همزمان از event-loop یا کتابخانه‌هایی مانند ReactPHP استفاده کنید تا کارایی بهتری داشته باشید.

نمونه بهبود یافته: خواندن امن با stream_select

<?php
$socket = stream_socket_server("udp://0.0.0.0:12345", $errno, $errstr, STREAM_SERVER_BIND);
stream_set_blocking($socket, false);

while (true) {
    $read = [$socket];
    $write = $except = null;
    $tv_sec = 5;
    $num = stream_select($read, $write, $except, $tv_sec);
    if ($num === false) {
        // خطای select
        break;
    } elseif ($num > 0) {
        $data = stream_socket_recvfrom($socket, 1500, 0, $peer);
        if ($data !== false && $data !== "") {
            echo "From $peer: $datan";
        }
    } else {
        // تایم‌اوت - هیچ داده‌ای دریافت نشد
        // کارهای دوره‌ای را اینجا انجام دهید
    }
}
?>

توضیح: این نسخه از stream_select استفاده می‌کند تا تنها زمانی که سوکت خواندنی است، اقدام به recv کند. این الگو برای برنامه‌های سروری که باید هم‌زمان چند کار انجام دهند مناسب است و از busy-loop جلوگیری می‌کند.

جمع‌بندی

تابع stream_socket_recvfrom ابزار ساده و قدرتمندی برای دریافت داده از استریم‌های شبکه‌ای است، به‌ویژه هنگام کار با UDP و نیاز به دانستن آدرس فرستنده. با درک رفتارهای خاص مانند رشتهٔ خالی برای EOF، نحوهٔ تنظیم حالت non-blocking، و استفاده از stream_select می‌توانید سرورهای مقاوم‌تر و بهینه‌تری بسازید. همواره تست بر روی سیستم عامل مقصد را فراموش نکنید تا از سازگاری پرچم‌ها و رفتارهای خاص اطمینان حاصل کنید.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: