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

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


دموی پروژه چت روم در سوکت

 چت روم و کار با سوکت ها در PHP

ارسال نظر