فرهاد مهریاری
Full Stack Web Developer

آموزش برنامه نویسی Web Socket در PHP

تعریف وب سوکت : یه پروتکلی هست بر مبنای TCP که یک ارتباط دو طرفه ( فول داپلکس [مضاعف ، دو رشته ای]) بین کلاینت و سرور ایجاد میکنه . کاربرد وب سوکت : بیشتر در چت و وب کنفرانس ها و در برنامه هایی که نیاز به ارتباط تنگاتنگ بین سرور و کلاینت هست یا مثلا تو یه قسمتی از برنامه برای چک کردن پست ، اطلاعیه و پیغام جدید و یا در بازی های تحت وب ، سازگاری وبسوکت : وب سوکت با مرور گر های کروم نسخه 14 به بعد موزیلا فایرفاکس نسخه 6 به بعد اپرا نسخه 12.10 به بعد و اینترنت اکسپلورر از نسخه 10 به بعد از وب سوکت پشتیبانی می کنند ویژگی جالب وب سوکت اینه که علاوه بر مرورگر از سایر زبان های برنامه نویسی به عنوان مثال Java یا Php می توان به عنوان کلاینت وب سوکت استفاده نمود.

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران

تفاوت عمده ای که وب سوکت با HTTP داره در اینه که در HTTP سرور بعد از ارسال response کانکشن موجود رو قطع میکنه ,ولی در وب سوکت بعد از پاسخ سرور کانکشن موجود همچنان برقرار هست و این کمک میکنه که هر وقت دیتای جدیدی بود باز به کلاینت ارسال کنه ، برای مثال برنامه داریم که هر 5 ثانیه یک بار برای وجود پیغام جدید درخواستی رو با متد POST برای سرور ارسال میکنه اگر در هر ارسال فقط 2 KB دیتا مصرف کنه در یک ماه میشه حدودا 1 GB ، ولی اگر از وب سوکت استفاده کنیم موقع اتصال انترنت یک کانکشن بین کلاینت و سرور ایجاد میشه و هر موقع پیغام جدیدی موجود بود برای کلاینت میفرسته

Web socket  VS. HTTP

متد های وب سوکت چه چیزهایی هستند؟

  • open : وقتی که وب سوکت وصل میشه
  • close : وقتی که وب سوکت بسته میشه
  • message : وقتی پیغامی دریافت میکنه
  • error : وقتی خطایی رخ میده

آدرس وب سوکت سرور چه ساختاری دارد؟

آموزش وب سوکت

همون طور که توی تصویر هم میبینید دیگه خبری از HTTP نیست ! یعنی وب سوکت خودش یه پروتکل مجزاست.در ادامه به نحوه ایجاد وب سوکت سرور در زبان PHP و وب سوکت کلاینت در Javascript خواهیم پرداخت.

وب سوکت سرور در php چیست؟

ابتدا در پوشه ی www سرور (easy php , wamp , xamp یا هر برنامه دیگه ای ) یک پوشه ای به نام websocket ایجاد می کنیم .سپس کتابخانه های رو که برای شروع نیاز داریم رو اونجا کپی می کنیم (در انتها تمامی فایل های مورد نیاز برای دانلود قرار داده شده است.) و یک فایلی به نام server.php ایجاد می کنیم.در فایل server.php کد های زیر رو قرار می دهیم :

<?php

include "PhpWebSocket.class.php";


$host="0.0.0.0";
$port=8000;
$Server = new PHPWebSocket();
$Server->bind('message', 'wsOnMessage');
$Server->bind('open', 'wsOnOpen');
$Server->bind('close', 'wsOnClose');
$Server->wsStartServer($host, $port);

function wsOnMessage($clientID, $message, $messageLength, $binary)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) said : $message." );
}

function wsOnOpen($clientID)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has connected." );
}

function wsOnClose($clientID, $status) {

	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has disconnected." );

}

حال عملکرد کد بالا رو خط به خط بررسی می کنیم : در خط سوم ما فقط کتابخانه PhpWebSocket.class.php رو به برنامه اضافه کردیم.در خط 6 آی پی سیستم رو وارد می کنیم که وب سوکت سرور در اون آی پی اجرا بشه . برای اینکه هم آی پی داخلی و هم آی پی تحت شبکه و حتی آی پی اکسترنال رو هرسه همزمان روی اون ها سرور اجرا بشه ما 0.0.0.0 رو وارد می کنیم که هر آی پی که باشه روی اون ها سرور اجرا بشه مزیت دوم این 0.0.0.0 در این هست که اگر آی پی سیستم تغییر کرد سرور خودکار از آی پی جدید استفاده می کنه.در خط 7 هم پورتی رو که سرور در اون اجرا میشه رو وارد کردیم برای مثال پورت 8000 که اکثرا خالی هست.

در خط 8 یک شی جدید از وب سوکت سرور ایجاد می کنیم و آن را در متغییر $server قرار می دهیم ، همونطور که در قسمت اول آموزش گفته بودیم که وب سوکت چندین متد داره (open , close , message ) اینجا ما متد ها رو تعریف کردیم و هرسه اون ها رو آبجکت سرور معرفی می کنیم یعنی در خط 9 تعریف می کنیم که وقتی متد message اتفاق می افته تابع wsOnMessage رو فراخوانی بکن خطوط 10و11 هم به همین ترتیب و نهایتا ما در خط 12 متد wsStartServer رو با پارامتر های ورودی آی پی سیستم و پورت فراخوانی می کنیم. پس از اجرای این کد وب سوکت ما شروع به کار می کنه در خطوط 14،21 و 28 هم ما توابعی رو که برای متد های سرور معرفی کردیم رو تعریف می کنیم.

wsOnOpen چیست؟

زمانی که کسی به وب سرور وصل میشه این تابع فراخوانی میشه و کد های موجود توی اون در سرور پیغام اتصال کاربر جدید به همراه آی پی رو نمایش میده.

wsOnMessage چیست؟

زمانی که کاربر وصل شده به سرور ، پیغامی ارسال می کنه این تابع فراخوانی میشه و شماره کاربر clientid و متن پیغام رو به همراه آی پی کاربر چاپ می کنه

wsOnClose چیست؟

زمانی که اتصال کاربر از سرور قطع میشه این تابع فراخوانی میشه و شماره کاربر رو که به عنوان پارامتر ورودی دریافت میکنه پس از چاپ ، از لیست کاربر های وصل شده حذف می کنه کد بالا رو که در فایل server.php ذخیره کردیم با استفاده از محیط command window با دستور php -q server.php اجرا می کنیم.برای اینکه در هربار اجرای سرور دستور بالا رو وارد نکین ، یک فایل server.bat بسازین و کد

php -q server.php

در اون قرار بدین بسپس با اجرای اون فایل سرور ما شروع به کار می کنه و شما در صورت اجرای درست تمامی مراحل باید تصویر زیر رو در محیط command window مشاهده کنید :

آموزش وب سوکت سرور در زبان php

خب در صورت مشاهده نتیجه بالا وب سوکت سرور به درستی ایجاد شده و منتظر دریافت کانکشن از طرف کاربر است.

وب سوکت کلاینت در javascript چیست؟

در پوشه ای دلخواه یک فایل index.html ایجاد کنید و کد زیر رو در اون قرار بدین :

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
    <head>
        <title>Farhad Mehryari | Websocket Client</title>
    </head>
    <body>
        <script>
                serverUrl = 'ws://127.0.0.1:8000/websocket/server.php';
                if (window.MozWebSocket) {
                    socket = new MozWebSocket(serverUrl);
                } else if (window.WebSocket) {
                    socket = new WebSocket(serverUrl);
                }

                  socket.binaryType = 'blob';
                  socket.onopen = function(msg) {
                     console.log("connected to socket");
                      return true;
                  };

                  socket.onmessage = function(msg) {
                      console.info(msg.data);
                      return true;
                  };
                  socket.onclose = function(msg) {
                    console.log("Bye Bye socket");
                      return true;
                  };

        </script>
    </body>
</html>

کد بالا رو بررسی می کنیم :  در متغییر ws آدرس سرور وب سوکت رو قرار میدیم در سطر بعدی بررسی می کنیم که مرور گر از وب سوکت پشتیبانی م ی کنه یا نه در قسمت اول آموزش گفتیم که مرور گر ها از چه نسخه ای به بعد از وب سوکت پشتیبانی می کنند.

بررسی می کنیم که MozWebSocket ( برای مرور گر موزیلا فایرفاکس ) موجود هست یا نه در غیر این صورت WebSocket رو بررسی می کنیم که اگر مقدار بازگشتی این شرط if هم false باشد یعنی مرور گر از وب سوکت پشتیبانی نمی کند. حال بعد از اتصال سه متد وب سوکت رو که در سرور هم بیان کردیم ، تعریف می کنیم.حال فایل index.html رو ذخیره کرده و در مرور گر اجرا می کنیم :

آموزش وب سوکت Php , Javascript

در تصویر بالا من فایل کلاینت رو در مرور گر گوگل کروم اجرا کردم حال همان فایل رو در مرور گر فایرفاکس نیز اجرا می کنم :

آموزش وب سوکت Php Javascript

مشاهده می کنیم که در خروجی سرور پیغام اتصال جدید دوباره با شماره جدید ظاهر شد. حال تغییری جزئی در فایل Index.html ایجاد می کنیم تا بتوانیم پیغامی به سرور ارسال کنیم : محتویات فایل Index.html رو به کد زیر تغییر بدین :

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
    <head>
        <title>Farhad Mehryari | Websocket Client</title>
    </head>
    <body>
      <input type="text" id="message"/>
      <input type="button" value="send !" onclick="sendmsg()" />
        <script>
                serverUrl = 'ws://127.0.0.1:8000/websocket/server.php';
                if (window.MozWebSocket) {
                    socket = new MozWebSocket(serverUrl);
                } else if (window.WebSocket) {
                    socket = new WebSocket(serverUrl);
                }

                  socket.binaryType = 'blob';
                  socket.onopen = function(msg) {
                     console.log("connected to socket");
                      return true;
                  };

                  socket.onmessage = function(msg) {
                      console.info(msg.data);
                      return true;
                  };
                  socket.onclose = function(msg) {
                    console.log("Bye Bye socket");
                      return true;
                  };

                  function sendmsg()
                  {
                    var msg=document.getElementById("message");
                    socket.send(msg.value);
                  }

        </script>
    </body>
</html>

حال سرور رو اجرا و فایل index.html رو در مرور گر اجرا می کنیم : بعد از اجرای فایل در مرور گر و اتصال به سرور ، متن Hi Server رو در فیلد موجود وارد کردم سپس کلید send رو زدم و مجددا متن I'am Sending Your Message وارد کردم دوباره send زدم خروجی در تصویر زیر مشاهده می کنیم :

آموزش وب سوکت سرور و کلاینت

حالا برای ارسال پیغام از طرف سرور به کلاینت و درک بهتر ارتباط سریع تغییراتی در فایل server.php و index.html اعمال می کنیم :

فایل server.php در قالب Web Socket

<?php

include "PhpWebSocket.class.php";

$host="0.0.0.0";
$port=8000;
$Server = new PHPWebSocket();
$Server->bind('message', 'wsOnMessage');
$Server->bind('open', 'wsOnOpen');
$Server->bind('close', 'wsOnClose');
$Server->wsStartServer($host, $port);

function wsOnMessage($clientID, $message, $messageLength, $binary)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) said : $message." );
	
	$Server->wsSend($clientID , "From Server : ". $message);
	
}

function wsOnOpen($clientID)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has connected." );
}

function wsOnClose($clientID, $status) {

	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has disconnected." );

}

تنهای دستوری که اضافه کردیم کد زیر است که به تابع wsOnMessage اضافه شد :

	$Server->wsSend($clientID , "From Server : ". $message);

دستور بالا هنگام دریافت پیغام از کلاینت ، به اون پیغامی رو ارسال می کنه : حال در فایل Index.html تغییرات زیر رو وارد می کنیم تا پیغامی های ارسالی از سرور رو مشاهده کنیم :

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
    <head>
        <title>Farhad Mehryari | Websocket Client</title>
    </head>
    <body>
      <textarea id="log" style="height:200px;width:300px;"></textarea>
      <br>
      <input type="text" id="message"/>
      <input type="button" value="send !" onclick="sendmsg()" />
        <script>
                var log=document.getElementById("log");
                serverUrl = 'ws://127.0.0.1:8000/websocket/server.php';
                if (window.MozWebSocket) {
                    socket = new MozWebSocket(serverUrl);
                } else if (window.WebSocket) {
                    socket = new WebSocket(serverUrl);
                }

                  socket.binaryType = 'blob';
                  socket.onopen = function(msg) {
                     log.value="> connected to socket\r\n";
                      return true;
                  };

                  socket.onmessage = function(msg) {
                      log.value+="> "+msg.data+"\r\n";
                      return true;
                  };
                  socket.onclose = function(msg) {
                    console.log("Bye Bye socket");
                      return true;
                  };

                  function sendmsg()
                  {
                    var msg=document.getElementById("message");
                    log.value+="> Me : "+msg.value+"\r\n";
                    socket.send(msg.value);
                  }

        </script>
    </body>
</html>

پس از ایجاعمال تغییرات گفته شده مجدد سرور رو اجرا کنید ، سپس فایل index.html رو در مرورگر اجرا کنید ، خروجی حاصل از تغییرات اعمال شده :

آموزش وب سوکت سرور ، کلاینت

در قسمت بعدی نحوه ی ایجاد یک سیستم گفتگو مبتنی بر وب سوکت رو با استفاده از php و javascript (طبق مفاهیم و متد های بیان شده در این آموزش ) خواهیم پرداخت.

بدون هیچ مقدمه ای بریم سر اصل مطلب ، در انتهای قسمت قبلی گفتیم که در قسمت سوم آموزش وب سوکت یه سیستم گفتگو (chat) مبتنی بر وب سوکت رو توسعه خواهیم داد. ابتدا قالب قسمت کاربر(client) رو با استفاده از html , css , js طراحی می کنیم که من یه قالب آماده از اینترنت برداشتم با کمی ویرایش به شکل زیر شد که فایل های این قالب و سایر قسمت ها در انتهای آموزش قابل دریافت هستند.

آموزش وب سوکت

البته تصویری که بالا میبینید زمانی رو نشون میده که سیستم چت ما تموم شده و در حال اجراست.برای شروع ما ابتدا نامی رو از کاربر میگیریم که در ساده ترین روش ممکن این کار رو میشه از طریق جاوااسکریپت به روش زیر انجام داد :

var name = prompt("enter your name :");

کد بالا را به ابتدای فایل index.js اضافه می کنیم سپس در همان فایل پس از کد زیر

$(document).ready(function() {

کد زیر را اضافه می کنیم

$("#myname").text(myname);

این کد نام گرفته شده از کاربر را در قسمت بالای پنجره گفتگو نمایش می دهد.حالا باید نام گرفته شده از کاربر به سرور مون ارسال کنیم که این کاربر تازه وارد شده رو به لیست سایر افراد موجود هم اضافه بکنه البته موقع ارسال اسم کاربر که به منزله ورود یک نفر جدید به گفتگو می باشد ، ما در قسمت سرور بررسی می کنیم که آیا قبل این نفر کاربر دیگه در گفتگو هست یا نه اگر بود این کاربر جدید رو به لیست اونها اضافه بکنه نبود که هیچ یه کار دیگه هم که باید بکنیم اینه که وقتی کاربر جدید وارد شد لیست نفرات قبلی رو هم که در سیستم آنلاین هستند رو بگیریم البته اگر قبلا نفری باشه نبود که هیچ توضیح زیاد شد بریم سراغ سرور :) کد زیر همون سروری بود که در قسمت قبل آموزش نوشته بودیم و کامل بررسی اش کردیم :

<?php

include "PhpWebSocket.class.php";

$host="0.0.0.0";
$port=8000;
$Server = new PHPWebSocket();
$Server->bind('message', 'wsOnMessage');
$Server->bind('open', 'wsOnOpen');
$Server->bind('close', 'wsOnClose');
$Server->wsStartServer($host, $port);

function wsOnMessage($clientID, $message, $messageLength, $binary)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) said : $message." );

	$Server->wsSend($clientID , "From Server : ". $message);

}

function wsOnOpen($clientID)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has connected." );
}

function wsOnClose($clientID, $status) {

	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has disconnected." );

}

اولین تغغیرات رو ما از تابع wsOnOpen می کنیم ، در قسمت قبل گفته شد که این تابع در سرور زمانی فراخوانی می شود که کاربر (client) جدیدی به سرور وصل شود.خب ما هنگامی که کاربری به سرور وصل میشه شماره کاربری ( client id) اون رو در آرایه ای ذخیره می کنیم که بعدا بتونیم متانسب با هر شماره کاربری اسم اون رو هم ذخیره بکنیم پس یه ارایه اس به اسم clients پس از کد زیر بدین صورت ایجاد میکنیم : نکته قابل توجه آرایه clients در این هست که این آرایه باید داخل یک کلاس باشه تا بتونیم اون رو از نوع static تعریف کنیم که در هر بار اتصال کاربر جدید محتویات قبلی اون که اطلاعات کاربران آنلاین شده قبلی هست پاک نشه

include "PhpWebSocket.class.php";
class core
{
	static $clients;
}

حالا در تابع wsOnOpen کد زیر رو اضافه میکنیم :

function wsOnOpen($clientID)
{
core::$clients[$clientID]=0;
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) has connected." );
}

اگر به کدی که اضافه کردیم دقت کنید ما در آرایه client اندیس شماره کاربر رو وارد کردیم و مقدار اون رو صفر قرار دادیم دلیل این کار این هست که چون ما بعدا نام کاربر رو ذخیره می کنیم ، و موقع اتصال کاربر به سرور نام کاربر رو نداریم الان صفر وارد می کنیم در مرحله بعدی نام کاربر رو در اندیس مربوط به خودش وارد می کنیم. حالا برمیگردیم به قسمت کلاینت و کد های مربوط به وصل شده به سرور رو اضافه کنیم کد زیر رو در فایل Index.js پس از اولین سطر اضافه می کنیم :

var log=document.getElementById("log");
serverUrl = 'ws://127.0.0.1:8800/chat_ws/server/server.php';
if (window.MozWebSocket) {
    socket = new MozWebSocket(serverUrl);
} else if (window.WebSocket) {
    socket = new WebSocket(serverUrl);
}

  socket.binaryType = 'blob';
  socket.onopen = function(msg) {
    console.log("connected");
      return true;
  };

  socket.onmessage = function(msg) {

      return true;
  };
  socket.onclose = function(msg) {
    console.log("Bye Bye socket");
    return true;
  };

  function sendmsg(f,t)
  {
  socket.send(f+"**"+t);
  }

کد بالا تقریبا همون کدی هست که در قسمت قبل توضیح داده شد البته با اندکی تفاوت که اون رو توضیح میدیم :  تابع sendmsg دو پارامتر ورودی داره یکی به عنوان تابع یکی به عنوان متن پیام ما تابع و متن پیام رو در یک متغییر قرار میدیم و بین اونها کارکتری مثل * یا هرچیزی قرار میدیم که توی سرور بتونیم اونا رو از هم جدا بکنیم و دلیل وجود چیزی به نام تابع برای این هست که ما زمانی می خایم از سرور لیست آنلاین ها رو بگیریم یا زمانی میخایم به سرور بگیم

که ما رو به لیست آنلاین های بقیه اضافه بکنه و زمانی میخایم گفتگو رو انجام بدیم برای اینکه سرور بتونه درخواست های ما رو بدرستی پاسخ بده به همراه متن یک تابع رو هم ارسال میکنیم وقتی که پیام به سرور رسید اونجا جداسازی میکنیم و برای هر تابع کد های مخصوص به اون می نوسیم و پاسخ رو به کاربر ارسال میکنیم ، خب حالا که با ضرورت وجود تابع آشنا شدیم اولین تابعی که نیاز داریم اینهست که نام کاربری کاربر رو در سرور ذخیره بکنه ، پس بعد از اتصال به سرور کد زیر رو اضافه می کنیم

socket.onopen = function(msg) {
    console.log("connected");
    sendmsg("save_name",myname);
      return true;
  };

سطری که ما اضافه کردیم پس از اتصال به سرور ، save_name رو به همراه نامی که کاربر وارد کرده رو به سرور ارسال میکنه ، حالا میریم سراغ سرور که در تابع wsOnMessage پیغام ارسالی از کاربر رو بگیریم ، تابع wsOnMessage رو که قبلا به این صورت نوشته بودیم :

global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) said : $message." );
	$Server->wsSend($clientID , "From Server : ". $message);

به صورت زیر تغییر میدیم

	global $Server;
	$data=explode("**",$message);
	$f=$data[0];
	$t=$data[1];
	switch ($f) {
		case 'save_name':
			core::$clients[$clientID]=$t;
		break;
	}

	$ip = long2ip( $Server->wsClients[$clientID][6] );
	$Server->log( "$ip ($clientID) said : $message." );


کاری که ما کردیم این بود که با استفاده از تابع explode در php ( که معادل این تابع در جاوا اسکریپت هم split هست) تابع و متن رو از هم جدا کردیم و هرکدوم رو متغییر های f و t قرار دادیم سپس با استفاده از تابع switch در php برای حالات مختلف متغییر f که همون تابع ما شرط گذاری کردیم و اولین شرط ما برای زمانی هست که تابع ارسالی از طرف کلاینت save_name هست که برای این قسمت همونطور که بالا اشاره شد در آرایه client در اندیس شماره کاربری که مقدار صفر رو ذخیره کردیم حالا بجای صفر نامی رو که کاربر برای خودش وارد کرده و الان در متغییر t اون رو در دسترس داریم قرار میدیم (طبق کد زیر ) :

$client[$clientID]=$t;

خب حالا نوبت به این میرسه که به بقیه کاربر ها (البته در صورتی که قبل از این کاربری به سرور وصل شده باشه) اعلام کنیم که یه کاربر جدید به گفتگو وارد شد به قول تلگرامی join شد :)  برای این کار ابتدا چک می کنیم که قبلا کاربری به سرور وصل شده یا نه اگر شرطمون صحیح بود به همه ی کاربر ها نام این کاربر جدید رو بفرستیم ، طبق کد زیر :

	core::$clients[$clientID]=$t;
			if ( sizeof($Server->wsClients) > 1 )
			{
				foreach ( $Server->wsClients as $id => $c )
					if ( $id != $clientID )
						$Server->wsSend($id, "new_join**$t");

حالا یه نکته ای که حائز اهمیته اینه که ما وقتی به تک تک کاربر ها عبارت join**username (اینجا منظور از username همان نامی است که کاربر برای خودش وارد کرده و در متغییر t موجوده) برای سایر کاربر ها ارسال کردیم همه اون عبارت رو دریافت می کنند ولی در قسمت کلاینت هم باید مثل سرور باید متغییری داشته باشیم که تابع رو از متن جدا کنیم و داخل اون قرار بدیم و سپس اون رو شرط گذاری بکنیم که مثلا وقتی سرور تابع newjoin رو برات فرستاد تو به لیست افراد آنلاین یکی رو اضافه کن دقیقا همون کاری که در قسمت سرور انجام دادیم پس در قسمت کلاینت و در تابع socket.onmessage که وقتی پیغامی از سمت سرور میاد این تابع فراخوانی میشه اینجا ما ابتدا جداسازی میکنیم و سپس با استفاده از switch حالات مختلف تابع رو بررسی میکنیم دقیقا مثل قسمت سرور ! مثل کد زیر :

socket.onmessage = function(msg) {
    data=msg.data.split("**");
    console.log(data);
      return true;
  };

متغییر msg.data همون پیغامی رو داره که سرور اونو میفرسته و ما تو سرور بین نام کاربری و اسم تابع که new_join بود دوتا ستاره گذاشتیم حالا اینجا با تابع split اونا رو از هم جدا می کنیم و میره (البته این کار و برعکس این هم که تو قسمت سرور انجام شد رو هم میشه با json انجام داد ولی برای من این ساده تر بود) در سطر بعدی توی کنسول لاگ میگیریم که چی از سرور اومد .  برای درک بهتر من سرور رو اجرا کردم ابتدا با مررگر chrome وارد شدم و اسم farhad رو زدم سپس با مرورگر opera رفتم و با اسم reza وارد شدم چون قبل از reza کسی به اسم farhad در سیستم وجود داره باید ورود و یا همون join شدن reza رو به فرهاد بگه که طبق تصویر زیر میبینید که توی مرورگر chrome که با farhad وارد شده توی کنسول ورود رضا از طرف سرور اعلام میشه

آموزش وب سوکت چت

حالا کاری که باید بکنیم اینه که توی جاوا اسکریپت یا با جی کوئری کدی بنویسیم که وقتی تابع ما new_join بود به لیست نفرات آنلاین این رو اضافه بکنه ، خب توی کد بالا کد لاگ کردن توی کنسول رو حذف میکنیم چون یه بار دیدیم که سرور درست میفرسته پس دیگه نیازی نداریم و در ادامه کد زیر رو اضافه می کنیم :

data=msg.data.split("**");
    switch (data[0]) {
      case 'new_join':
        friendData='<li><img width="50" height="50" src="img/user.png">';
          friendData+='<div class="info">';
            friendData+='<div class="user">'+data[1]+'</div>';
            friendData+='<div class="status on"> online</div>';
          friendData+='</div>';
        friendData+='</li>';
          $(".list-friends").append(friendData);
        break;
      default:

بعد از قرار دادن کد بالا و تست گرفتن خواهید دید که وقتی نفر جدید وارد گفتگو میشه به لیست نفرات آنلاین قبلی اضافه میشه ، حالا یه کار دیگه مونده اونم اینه که وقتی نفر جدید وارد شد لیست آنلاین های قبلی رو هم سرور برای اون بفرسته ، برای این کار برمیگردیم سراغ سرور دقیقا همون جایی که اگر قبل این کاربر جدید ، کاربر قبلی بود ورودش رو اعلام بکنه بعد از اون کد ما با استفاده از اون آرایه client که اطلاعات بقیه کاربر ها رو داره اون ها رو به کاربر تازه وارد شده میفرستیم به کد زیر توجه بنمایییید : در واقع کد نهایی قسمت save_name به صورت زیر می شود :

case 'save_name':
			core::$clients[$clientID]=$t;
			if ( sizeof($Server->wsClients) > 1 )
			{
				foreach ( $Server->wsClients as $id => $c )
					if ( $id != $clientID )
						$Server->wsSend($id, "new_join**$t");

				foreach(core::$clients as $cID=>$name)
				{
					if ($cID!=$clientID)
					$Server->wsSend($clientID, "online_list**$name");
				}
			}
		break;

که تابع Online_list هم اسامی کاربران موجود به غیر از خودش رو برای اضافه کردن به لیست کاربر تازه وارد شده میفرسته ، حالا میریم به قسمت کلاینت تا لیست نفرات آنلاین رو وقتی کاربر جدید وارد شد توی نوار سمت چپی نمایش بدیم ، کد زیر رو اضافه می کنیم (برای همون سوئیچی که قبلا در قسمت کلاینت نوشتیم) :

case "online_list" :
        friendData='<li><img width="50" height="50" src="img/user.png">';
          friendData+='<div class="info">';
            friendData+='<div class="user">'+data[1]+'</div>';
            friendData+='<div class="status on"> online</div>';
          friendData+='</div>';
        friendData+='</li>';
          $(".list-friends").append(friendData);
      break;

هرچند کد این تابع با تابع new_join یکی هست ولی بهتره که جدا باشند شاید در آینده یه تغییری ایجاد شد پس احتیاط لازم هست که از بهم ریختگی کد ها پیشگیری کنیم (پیشگیری بهتر از درمان D: ) خب تا اینجا لیست نفرات آنلاین تمام هست وقتی کاربری میاد ورودش به نفرات قبلی اعلام میشه و در لیست اونا قرار میگیره و لیست نفرات آنلاین قبلی هم توی لیست کاربر تازه وارد شده قرار میگیره ، خب یه نفس عمیق بکشید :) خستگی در کنید بریم سراغ خود گفتگو (chat)  کار زیادی نمونده توی دوتا سوئیچمون دوتا حالت در نظر میگیریم برای وقتی که تابع ما مقدارش برابر new_message باشه ، خب اول از قسمت کلاینت شروع می کنیم که کاربر وقتی پیغامی رو نوشت و send رو زد ابتدا توی صفحه ی گفتگو خودش بیفته و سپس اون رو به سرور ارسال بکنه سرور هم به بقیه ارسال بکنه

میریم سراغ فایل index.js

کد زیر باعث میشه وقتی کلید Enter یا روی دکمه ی send کلیک میکنیم تابع insertI رو فراخوانی بکنه

  $("#texxt").keypress(function(e) {
      if (e.keyCode === 13) {
        insertI();
        return false;
      }
    });

خب حالا بریم سراغ تابع insertI

خیلی کد خاصی نداره ! فقط ابتدا چک میکنه که محتوای فیلد ورودی اگر خالی نباشه مقدارش رو برداره و تو صفحه گفتگو نمایش بده سپس اونو به سرور هم بفرسته و نهایتا مقدار فیلد ورودی رو خالی بکنه

insertI = function() {
    var innerText;
    innerText = $.trim($("#texxt").val());
    if (innerText !== "") {
      $(".messages").append("<li class=\"i\"><div class=\"head\"><span class=\"time\">" + (new Date().getHours()) + ":" + (new Date().getMinutes()) + " AM, Today</span><span class=\"name\"> "+myname+"</span></div><div class=\"message\">" + innerText + "</div></li>");
      claerResizeScroll();
      sendmsg("new_message",innerText);
    }
  };

خب حالا ما وقتی یه چیزی تایپ کنیم و Enter بزنیم تو گفتگو نشون داده میشه و اونو به سرور هم ارسال میکنه حالا میریم سراغ قسمت سرور و کدی رو می نویسیم برای زمانی که تابع new_message شد متن ارسالی رو به سایر کاربر ها ارسال بکنه

case 'new_message' :
		if ( sizeof($Server->wsClients) > 1 )
		{
			foreach ( $Server->wsClients as $id => $c )
				if ( $id != $clientID )
					$Server->wsSend($id, "new_message**".core::$clients[$clientID]."**$t");
		}
		break;

کد بالا دیگه باید براتون مفهوم باشه فقط نکته ای که وجود داره ما اینجا ابتدا new_message و سپس نام کاربر (کسی که پیام میفرسته برای گفتگو) و در آخر متن ارسالی کاربر رو در یک عبارت قرار میدیم و بینشون ** میزاریم که بعدا توی کلاینت جداسازی بکنیم یعنی این بار ما سه تا پارامتر میفرستیم به کلاینت خب حالا توی سوئیچ کلاینت هم به صورت زیر عمل می کنیم :

case "new_message" :
        $(".messages").append("<li class=\"friend-with-a-SVAGina\"><div class=\"head\"><span class=\"time\">" + (new Date().getHours()) + ":" + (new Date().getMinutes()) + " AM, Today</span><span class=\"name\"> " + data[1] + "</span></div><div class=\"message\">" + data[2] + "</div></li>");
      break;

خیلی ساده ! ما سه تا پارامتر ارسال کردیم و این دفعه توی آرایه data ما سه تا مقدار وجود داره که به ترتیبت تابع ، نام کاربر اسرال کننده پیام و متن ارسالی کاربر هست . خب کار تمومه الان وقتی کاربر پیامی رو ارسال میکنه به بقیه هم میره ! خب این سیستم چت ی که ما نوشتیم حداقل میشه گفت تا به امروز سریع ترین روش ممکن برای توسعه سیستم گفتگو هست . البته یه جزئی تغییرات میخاد (مثل ثبت تاریخ و زمان شمسی و اینکه وقتی کاربری خارج میشه اونو از لیست آنلاین ها حذف بکنه که با توجه به مطالبی که گفته شد نوشتنش براتون خیلی ساده اس که البته اگر عمری باقی بود نسخه دوم این که ثبت نام و عضویت و ... هم باشه به اضافه این دو مورد رو ، توسعه میدیم ) انگار زیاد شد : | اگر سوالی داشتین بپرسین


    فرهاد مهریاری
    فرهاد مهریاری

    Full Stack Web Developer

    A simple developer!

    13 مرداد 1394 این مطلب را ارسال کرده

    نظرات