کار با سوکت ها ( 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
ارسال نظر