کار با سوکت ها ( Socket Programming ) ساخت چت روم در PHP

کار با سوکت ها در PHP – در این مقاله و پروژه ی آموزشی قصد داریم که از Socket ها به صورت کاربردی استفاده کرده و یک چت روم ساده با آن بسازیم تا درک بهتری از آن پیدا کنید .
در این آموزش با توابع زیر کار خواهیم کرد که مرتبط با Socket ها است :
1- socket_create
2- socket_bind
3- socket_listen
4- socket_set_nonblock
5- socket_accept
6- socket_write
7- socket_recv
8- gethostbyname
9- is_resource
خیلی خوب قبل از هر کاری یک توضیح شفاف واضح در مورد سوکت ارائه بدم ، کار با سوکت ها
سوکت ها در سطح پایین از شبکه برای تعامل و ارتباط بین کلاینت و سرور ساخته شده و مبدا پیدایش اون از سیستم عامل یونیکس بوده که در اکثر زبان های برنامه نویسی برای انتقال داده و اطلاعات استفاده می شود .
کد کامل پروژه چت روم
توضیحات در کد ها قرار داده شده :
function getCurrentTime() {
date_default_timezone_set("Asia/Tehran");
$client_time_content = date("Y/m/d H:i:s");
return $client_time_content;
}
function check_user_message($client_id, $raw_data) {
global $list_temp_user_message_data;
global $static_timestamp;
$replace_break_line = "%___N_{$static_timestamp}_N___%";
$raw_data = str_replace("\r\n", $replace_break_line, $raw_data);
$line_breaker = substr($raw_data, (strlen($replace_break_line) * -1));
$is_end_with_break_line = $replace_break_line === $line_breaker ? true : false;
$curren_client_list_data = $list_temp_user_message_data[$client_id];
if (!in_array($raw_data, $curren_client_list_data)) {
if ($is_end_with_break_line) {
$finall_data = explode($replace_break_line, $raw_data);
if (empty($finall_data[1])) {
$finall_data = $finall_data[0];
} else {
$finall_data = $finall_data[count($finall_data) - 2];
}
$curren_client_list_data[] = $raw_data;
$list_temp_user_message_data[$client_id] = $curren_client_list_data;
return "{$finall_data}";
} else {
return false;
}
}
return false;
}
set_time_limit(0);
$host_ip = '127.0.0.1';
$port = 1011;
echo "PHP Socket server Running on {$host_ip}:{$port}\n";
$socket_handshake = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket_handshake, $host_ip, $port);
socket_listen($socket_handshake);
socket_set_nonblock($socket_handshake);
$clients = [];
$list_temp_user_message_data = [];
$static_timestamp = time();
$counter_client = 0;
while (true) {
if ($client_socket = socket_accept($socket_handshake)) {
if (is_resource($client_socket)) {
$counter_client++;
$id_client = $counter_client;
$msg = "welcome to PHP Socket server ! \n";
socket_write($client_socket, $msg, strlen($msg));
$client_name = "(client{$id_client})>";
socket_write($client_socket, $client_name, strlen($client_name));
socket_set_nonblock($client_socket);
$clients[] = $client_socket;
$list_temp_user_message_data[] = array();
echo "new Client Added ! at " . getCurrentTime() . "\n";
}
}
foreach ($clients as $client_key_id => $client) {
if (!is_bool($client) && socket_recv($client, $client_content, 8192, MSG_PEEK)) {
$client_content = check_user_message($client_key_id, $client_content);
if (is_bool($client_content)) {
continue;
}
$client_content = trim($client_content);
if (empty($client_content)) {
continue;
}
$id_client = $client_key_id + 1;
$client_name = "(client{$id_client})>";
if ($client_content === 'quit') {
socket_close($client);
unset($clients[$client_key_id]);
unset($list_temp_user_message_data[$client_key_id]);
echo "{$client_name} left the chatroom !\n";
break;
}
$client_time_content = getCurrentTime();
$talkback_client = "{$client_time_content}\n";
$talkback_server = "{$client_name}{$client_content} at {$client_time_content}\n";
echo $talkback_server;
socket_write($client, $talkback_client, strlen($talkback_client));
socket_write($client, $client_name, strlen($client_name));
}
}
}
socket_close($socket_handshake);
لاین 47 : از آن جایی که ارتباط بین کلاینت و سرور به صورت stream برقرار هست یعنی بدون هیچ تاخیری اطلاعات ارسال می شود باید از تابع set_time_limit استفاده کنیم که php محدودیت زمانی برای اجرا برنامه نداشته باشه و بدون محدودیت کد ها رو پردازش کنه .
لاین 49 و 51 : آیپی و پورت ( باز) سروری که اسکریپت مون رو داره اجرا می کنه . کار با سوکت ها
لاین 55 : با تابع socket_create سوکت ها رو بر اساس پروتکل های داده شده آماده می کنیم . دارای 3 ورودی :
1- از چه نوع آیپی استفاده می کند ( ipv4 , ipv6 ) و یا از پروتکل یونیکس که آیپی ورژن 4 مورد استفاده قرار می گیره در این پروژه
2- اینکه از چه نوع تعاملی برخوردار باشه که از SOCK_STREAM استفاده می کنیم ، ارتباط مون به صورت مستقیم یا با اصطلاح real time باشه.
3- بر اساس ورودی اول این ورودی رو بدید که معمولا پروتکل tcp هست که می تونیم ورودی SOL_TCP رو بدیم .
بعد از اینکه با موفقیت تابع اجرا شد یک خروجی از نوع داده resource دریافت می کنیم که در جلو تر به کار مون میاد .
لاین 57 : کار این تابع این هست که در صورتی که به آیپی و پورت داده شده درخواستی از کلاینت وارد شده پاسخ بده و دارای 3 ورودی است که :
1- سوکت دریافت شده از تابع socket_create رو در این جا وارد می کنیم .
2- آیپی
3- پورت
لاین 59 : این که در حالت آماده باش قرار بگیره و 2 ورودی داره :
1- socket
2- حداکثر کلاینتی که سرور بتونه پاسخ بده ( البته در حالتی که ما داریم استفاده می کنیم ، این ورودی به کار نمیاد )
لاین 61 : این که پاسخ دهی سرور رو حالت non block باشه یعنی به صورت همزمان به چند کلاینت پاسخ بده .
دستورات دیگر داخل حلقه خواهد بود که در microsecond پردازش میشه .
کد های مربوط به تبادل اطلاعات بین کلاینت / سرور
لاین 70 : بعد از این که کلاینتی در خواستی فرستاد از این خط به بعد مربوط درخواست و پاسخ کلاینت میشه که با تابع socket_accept به درخواست کلاینت جواب مثبت میدیم و اجازه دسترسی به سرور . یک ورودی که همان خروجی لاین 55 هست . دقت داشته باشید که خروجی بدست آمده از socket_accept منحصر به فرد بود و مربوط به همان کلاینتی بود که به سرورمان درخواست فرستاده .
لاین 71 : با استفاده از این تابع مطمئن می شیم که خروجی بدست آمده از socket_accept موفقیت آمیز بود و حتما یک resource هست .
لاین 78 : با تابع socket_write به سمت کلاینتی که به سرورمان وصل شده پیغام می فرستیم که 3 ورودی دارد :
1- سوکت بدست آمده از خروجی socket_accept
2- پیغامی که میخواهیم بفرستیم
3- طول پیغام مان
لاین 82 : مجدد سوکت کلاینت را روی حالت non block می گذاریم که در صورتی که خواستیم از کلاینت ورودی دریافت کنیم . روی حالت block نباشه که باعث میشه که سرور نتواند به صورت همزمان پاسخ دهد . ( مهم )
لاین 93 : با تابع socket_recv ورودی های کلاینت را دریافت می کنیم . 4 ورودی :
1- سوکت بدست آمده از خروجی socket_accept
2- پیام کلاینت در متغیر داده شده ذخیره می شود
3- این که چه مقدار از پیغام کلاینت دریافت شود ( بر حسب بایت )
4- بر اساس چه استانداردی خروجی کلاینت دریافت شود بر روی MSG_PEEK قرار دهید که در هر چرخش حلقه while ورودی را بررسی کند .
لاین 95 : از آنجایی که تابع socket_recv بدون نیاز به وارد کردن دکمه Enter داده را دریافت می کند باید راه حلی داشته باشیم در صورتی که کاربر دکمه را فشرد پیغام را دریافت کند . که باینری آن می شود :
\r\n
ما در تابع check_user_message بر اساس این ورودی ها رشته را بر مبنای همین کد باینری تقسیم کرده و در صورتی که با کد باینری تمام شد سرور متوجه می شود که کلاینت دکمه Enter را زده است .
لاین 110 : بر خروج کلاینت مکانیزمی را تعبیه می کنیم که اگر کاربر پیغام quit را وارد کرد آن را از چت روم خارج کنیم .
لاین 111 : با استفاده از تابع socket_close ارتباط را با کلاینت قطع می کنیم .
لاین 118 : با تابع getCurrentTime زمان بر اساس منطقه زمانی ایران دریافت کرده و از آن در نمایش تبادل پیام ها استفاده کنیم .
اجرای پروژه چت روم
باید از telnet استفاده کنید که در ویندوز به صورت پیشفرض غیرفعال است برای فعال سازی آن وارد cmd شده ( البته Run as Administrator ) و سپس دستور زیر را وارد کنید :
dism /online /Enable-Feature /FeatureName:TelnetClient
حال کد بالا را در فایلی با نام server.php در مسیر Desktop ذخیره کنید و سپس cmd را باز کرده و دستورات زیر را وارد کنید :
cd Desktop php server.php
حال یک cmd دیگر را باز کرده و دستور زیر را وارد کنید :
telnet 127.0.0.1 1011
دموی پروژه چت روم در سوکت

ارسال نظر