ساخت سرور چت TCP با NodeJs
چهارشنبه 6 تیر 1397در این مقاله یک سرور TCP با کمک node خواهیم ساخت. قدم بعدی را با ساخت یک سرور چت ساده پیش میبریم که اتصالات بسیاری که کلاینتها را قادر میسازد تا با یکدیگر ارتباط برقرار کنند را مدیریت میکند.
TCP
قبل از شروع به کار بیایید در مورد TCP و مزایای آن صحبت کنیم. TCP (پروتکل کنترل انتقال) یکی از پروتکلهای اصلی اینترنت است، در لایه حمل و نقل مدل TCP/IP وجود دارد، TCP بالای لایه شبکه قرار میگیرد و یک مکانیزم حمل و نقل برای پروتکلهای لایه کاربرد مثل HTTP، SMTP، IRC و غیره فراهم میکند، TCP به دلیل ویژگیهای زیر یک مکانیزم حمل و نقل گسترده است:
در صورتی که به یادگیری اصولی و حرفه ای این تکنولوژی قدرتمند علاقمند هستید میتوانید دوره کامل و جامع آموزش Node Js موجود در سایت تاپ لرن را مشاهده کنید .
1. قابل اطمینان: بیتها به ترتیبی که فرستاده شدهاند منتقل میشوند. همچنین مکانیزمهایی مثل تأییدیهها هنگامی که بیتها تحویل داده شدهاند و handshakeها را ترکیب میکند.
2. اتصالگرا: یک راهاندازی بین فرآیندهای سرور و کلاینت مورد نیاز است.
3. کنترل جریان: کنترل جریان بسته ها بین دو انتها وجود دارد تا اطمینان حاصل شود که هیچ یک از طرفین از بین نرفته و تعادل حفظ شده است.
4. کنترل ازدحام: وقتی که شبکه شلوغ است، راه فرستنده بسته میشود.
TCP Server
حالا که درک بهتری از TCP و مزایای آن داریم، بیایید TCP سرور خودمان را با node بسازیم. خوشبختانه node این امکان را از طریق یک ماژول هسته به نام net ایجاد میکند.
//Require the 'net' module const net = require('net); //Store the 'net.Server' object returned by 'net.createServer()' const server = net.createServer(); //Add event listener for connection events server.on('connection', connection => { }); //Add event listener for close events server.on('close' () => { console.log(`Server disconnected`); }); //Add listener for error events server.on('error', error => { console.log(`Error : ${error}`); }); //Listen for connections on port 4000 server.listen(4000);
net.createServer یک شیء net.Server که از کلاس Event emitter ارثبری میکند را برمیگرداند، امکان انتشار رویدادهایی مثل close، error، connection و غیره را ایجاد میکند.
وقتی کلاینت به سرور متصل میشود، رویداد connection منتشر میشود و شیء net.Socket به عنوان یکی از آرگومانها در callback تابعی که نشاندهنده کلاینتی است که فقط متصل است پاس داده میشود. net.socket یک جریان دوتایی است (میتوانیم از آن بخوانیم و در آن بنویسیم) و همچنین از کلاس Event emitter ارثبری کرده است.
بیایید یک برنامه چت بسازیم تا ببینیم چگونه همه این بخشها به صورت معنادار در کنار هم قرار میگیرند، اما بهتر است قبل از آن، معرفی مختصری در مورد پروتکل telnet داشته باشیم.
Telnet
Telnet یک پروتکل بسیار قدیمی است، اگرچه امروزه به طور عمده توسط SSH جایگزین شده است، هنوز هم همراه با هر سیستم عامل اصلی میآید و یک ترمینال مجازی و بسیاری از ویژگیها را فراهم میکند. Telnet یک لایه بالاتر از پروتکل TCP قرار میگیرد، بنابراین به جای ایجاد یک کلاینت TCP، ما از آن برای برنامه چت خود استفاده میکنیم تا چیزها تا حد ممکن ساده باشند. حالا بیاید آن را بسازیم.
سرور چت
ما قبلا یک سرور TCP ایجاد کردیم، که اساسا همه آن چیزی است که انجام میشود، مهمترین مواردی که در حال حاضر اجرا میشوند عملکردهای مربوط به اتصال کلاینتها از طریق telnet هستند. بنابراین اولین عملکردی که ما میخواهیم این است که وقتی اتصالی رخ میدهد کلاینت نام کاربری را وارد کند. ما باید کلیدهای ارسال شده توسط کلاینت را بگیریم، این امر با افزودن یک شنونده رویداد data بر روی شیء connection ما امکانپذیر است. این رویداد data هر زمان که کاربر کلیدی را فشار دهد رخ میدهد. همچنین باید هر اتصال فعال در یک شیء را ذخیره کنیم.
نکته: در سیستم عامل لینوکس که در حال اجرای telnet است، رویداد data تنها زمانی صادر میشود که کلید enter فشرده شود اما در سایر سیستم عاملها، رویداد data هر زمان که کلاینت هر کلیدی را فشار دهد انتشار مییابد. بنابراین نیاز به شیوهای داریم تا آن را توسط گرفتن کلید ورودی، تنها زمانی که کلاینت کلید enter را فشار میدهد، که توسط کاراکتر ‘\r\n’ نشان داده میشود، اصلاح کنیم. همچنین آنها را وقتی کاررا با آن انجام می دهیم، کنار میگذاریم.
.... //All active connections are stored in this object together with their client name let clients = {} //Stores the number of active clients let clientCount = 0; server.on('connection', connection => { let clientname //Keep track of keystrokes let message = []; //Remember `net.Socket` is a dublex stream, so we can write into it connection.write(`Please enter a room name\r\n`); //data is sent in buffers so we want to use the utf-8 character encoding to make it a readable format connection.setEncoding('utf-8'); //Listen for input from the client connection.on('data', data => { //Push every keystroke into an array message.push(data); //Proceed only if the 'enter' key has been pressed if( data == '\r\n'){ //Join the keystrokes stored and remove the 'enter' character let clientInput = message.join('').replace('\r\n',''); //Proceed if client name does not exist if(!clientname){ //Check whether client name is already taken if( clients[clientInput] ){ connection.write(' - Name is taken, try another name\r\n') //Discard the previous keystrokes the client entered message = []; return; } else { //Store the client name clientname = clientInput; //Increase the number of active clients clientCount++; //Store the connections by the name entered clients[clientInput] = connection; //Welcome the client connection.write(` - Welcome to the Chatbox, There are ${clientCount} active users\r\n`); //Discard the previous keystrokes the client entered message = []; } } else { //We'll get back here } } }) }) ....
بنابراین در حال حاضر ما قادر به اجرای چند عملکرد هستیم:
ارسال پیام زمانی که کلاینت کلید enter را فشار میدهد
نگه داشتن مسیر همه کلاینتهای فعال
اطمینان از اینکه هر کلاینت یک نام منحصربهفرد دارد
بنابراین آنچه در حال حاضر باقی میماند این است که این عملکردهای باقیمانده را اجرا کنیم:
پخش/ارسال پیام به همه کلاینتهای فعال دیگر
حذف اتصال شیء، وقتی اتصال کلاینت قطع میشود
مدیریت رویدادهای خطا
... //Helper function that helps us send a message to every other client connected //We'll see how this works when we put everything together function broadcast( msg ){ //Loop through the active clients object for( let user in clients ){ //Send message to all active clients except yourself if( clients[ user ] !== connection ){ clients[ user ].write(msg); } } } //A close event is emitted when a connection has disconnected from the server connection.on('close', () => { //When a client disconnects, remove the name and connection delete clients[clientname]; //Decrease the active client count clientCount--; //Send a message to every active client that someone just left the chat }) //Handle error events connection.on('error', error => { connection.write(`Error : ${error}`); } ...
بنابراین حالا که توانستهایم تمام عملکردهای مورد نیاز سرور چت خود و نحوه مدیریت اتصالات را پیادهسازی کنیم، بیاید تمام این موارد را در کنار هم قرار داده و برنامهیمان را تست کنیم.
const net = require('net); const server = net.createServer(); //All active connections are stored in this object together with their client name let clients = {} //Stores the number of active clients let clientCount = 0; server.on('Connection', Connection => { let clientname; let message = []; //Helper function that helps us send a message to every other client connected function broadcast( msg ){ //Loop through the active clients object for( let client in clients ){ //Send message to all active clients except yourself if( clients[client] !== connection ){ clients[client].write(msg); } } } connection.write(`Please enter a room name\r\n`); connection.setEncoding('utf-8'); connection.on('data', data => { //Push every keystroke into an array message.push(data); //Proceed only if the 'enter' key has been pressed if( data == '\r\n'){ //Join the keystrokes stored and remove the 'enter' character let clientInput = message.join('').replace('\r\n',''); //Proceed if client name does not exist if(!clientname){ //Check whether client name is already taken if( clients[clientInput] ){ connection.write(' - Name is taken, try another name\r\n') //Discard of the previous keystrokes the client entered message = []; return; } else { //Store the client name clientname = clientInput; //Increase the number of active clients clientCount++; //Store the connections by client name clients[clientInput] = connection; //Welcome the client connection.write(` - Welcome to the Chatbox, There are ${clientCount} active users\r\n`); //Send a message to every active client that someone just joined the room broadcast(` - ${clientname} has joined the room\r\n`); //Discard the previous keystrokes the client entered message = []; } } else { //Send the message received to every client broadcast(` > ${clientname} : ${clientInput}\r\n`); //Discard the previous keystrokes the client entered message = []; } } }) //A close event is emmited when a connection is disconnected from the server connection.on('close', () => { //When a client disconnecs, remove the name and connection delete clients[clientname]; //Decrease the active client count clientCount--; //Send a message to every active client that someone just left the room broadcast(` - ${clientname} has left the room\r\n Active Users : ${clientCount}\r\n`); }) //Handle error events connection.on('error', error => { connection.write(`Error : ${error}`); } }); server.on('close' () => { console.log(`Server disconnected`) }); server.on('error', error => { console.log(`Error : ${error}`); }); server.listen(4000);
اکنون که ما همه چیز را با هم داریم، بیایید پیش رویم و آن را امتحان کنیم.
ترمینال خود را باز کرده و دستور زیر را تایپ کنید.
$ telnet localhost 4000
نام و بقیه موارد از شما خواسته می شود و شما قادر خواهید بود تا پیامها را به کلاینتهای فعال دیگر ارسال کنید، بنابراین ادامه دهید و چند ترمینال را باز کنید و به سرور TCP خود متصل شوید، ویژگیهای چت خود را تست کنید، این برای ماست:
نتیجهگیری
ما توانستیم از net ماژول هسته node برای ساخت سرور TCP استفاده کنیم و متعاقبا یک سرور چت با telnet به عنوان کلاینت TCP خود بسازیم، ماژول net دارای ویژگیهای بسیاری است، از جمله اینکه شما میتوانید از آن برای ساخت برنامه کلاینت IRC استفاده کنید، و همچنین موارد دیگر. به هر حال ما یک سرور TCP که اتصالات بسیاری را مدیریت میکند و برای کلاینتها سودمند است تا در یک فرم با یکدیگر ارتباط برقرار کنند، ساختیم، که این مسأله درک بهتری از نحوه استفاده از ماژول net به ما میدهد.
- NodeJs
- 3k بازدید
- 1 تشکر