آموزش Socket.io

چندین راه برای ایجاد برنامه‌های ارتباطی real-time وجود دارد. دو مورد از شناخته شده‌ترین راه‌ها برای این کار، استفاده از HTTP Long Polling و یا WebSocketها است. در این مقاله خلاصه‌ای از مقایسه میان این دو را مطالعه خواهید کرد. اما تصمیم گرفتیم که در این مقاله بر روی WebSocketها تمرکز کنیم. به این دلیل این مقاله آموزش Socket.io برای ساخت یک برنامه چت real-time است.WebSockets چیست؟WebSockets API یک تکنولوژی است که یک کانال دو طرفه برای ارتباط میان کلاینت و سرور را فراهم می‌کند. این بدان معنی است که دیگر لازم نیست کلاینت، به هنگام درخواست دیتا از سرور، آغاز کننده transaction باشد.به هنگام ایجاد اولین درخواست به سمت سرور، به غیر از دریافت دیتا، یک ارتباط WebSocket ایجاد می‌کند. این فرآیند، تحت عنوان WebSocket handshake نیز شناخته می‌شود. هدر Upgrade نیز در درخواست وجود دارد. این روشی است که کلاینت به سرور اطلاع می‌دهد که نیاز دارد اتصالی را ایجاد کند.WebSockets در مقابل HTTPبنابراین، آیا این بدان معنی است که ایجاد برنامه‌های چت و گفت‌وگو، بدون WebSockets غیرممکن است؟بسیار خب، تکنیکی تحت عنوان HTTP Long Polling وجود دارد. با استفاده از این، کلاینت درخواستی را به سمت سرور ارسال می‌کند و سرور این اتصال را تا زمانی که دیتا جدیدی در دسترس باشد، باز نگه می‌دارد. به هنگامی که دیتا در دسترس قرار بگیرد، کلاینت آن را دریافت می‌کند، سپس درخواست جدید ایجاد و ارسال می‌شود، این عملیات بار‌ها و بار‌ها تکرار می‌شود. به هر حال، یک عیب بزرگ در استفاده از HTTP Long Polling، استفاده زیاد از منابع سرور است.ایجاد یک برنامه چت توسط Socket.ioدر ادامه به طور خلاصه به شما آموزش می‌دهیم که چگونه توسط Socket.io، به همراه Vanilla JS برای فرانت‌اند و NodeJS به عنوان سرور، یک برنامه چت ساده بسازید. ابتدا نیاز دارید که یک پروژه به همراه فایل package.json ایجاد کنید:بعد از ایجاد پروژه، نیاز است که سرور را راه‌اندازی و تمام وابستگی‌ها را نصب کنید. برای انجام این کار، ابتدا فایل index.js را ایجاد و socket.io و express را نصب کنید. برای این کار، می‌توانید از دستور زیر استفاده کنید:touch index.js && npm install express socket.io && npm install --save-dev nodemonدر فایل index.js، نیاز است که سرور لوکال را راه‌اندازی و تنظیمات پایه‌ای برای اتصال سوکت‌ها را ایجاد کنید. اساسا در این لحظه، این تمام کاری است که باید در سمت بک‌اند انجام دهید. به هنگامی که یک درخواست مناسب ایجاد کنید، باید اتصال ایجاد شود. این موضوع توسط logیی که توسط برنامه ایجاد می‌شود، قابل اثبات خواهد بود:const express = require(&quotexpress&quot); const socket = require(&quotsocket.io&quot); // App setup const PORT = 5000; const app = express(); const server = app.listen(PORT, function () { console.log(`Listening on port ${PORT}`); console.log(`http://localhost:${PORT}`); }); // Static files app.use(express.static(&quotpublic&quot)); // Socket setup const io = socket(server); io.on(&quotconnection&quot, function (socket) { console.log(&quotMade socket connection&quot); });حالا نیاز است که سمت فرانت‌اند را آماده کنید. ابتدا با ایجاد پوشه public به همراه فایل‌های index.html و main.js، این کار را شروع می‌کنیم. اگر نیاز داشته باشید، می‌توانید فایل style.css را نیز ایجاد کنید.در زیر، ساختار و محتویات فایل index.html را مشاهده خواهید کرد. اساسا تمام نیاز شما، چندین تگ HTML به همراه یک سری اسکریپت از Socket.io، خواهد بود. فایل index.html شما، به همراه اسکریپت‌های WebSockets، باید چیزی شبیه کد زیر باشد:<!DOCTYPE html> <html lang=&quoten&quot> <head> <meta charset=&quotUTF-8&quot /> <meta name=&quotviewport&quot content=&quotwidth=device-width, initial-scale=1.0&quot /> <title>Socket.io simple chat</title> <link rel=&quotstylesheet&quot href=&quot./style.css&quot /> </head> <body> <div class=&quotcontainer&quot> <div class=&quotinbox&quot> <div class=&quotinbox__people&quot> <h4>Active users</h4> </div> <div class=&quotinbox__messages&quot> <div class=&quotmessages__history&quot></div> <div class=&quotfallback&quot></div> </div> </div> <form class=&quotmessage_form&quot> <input type=&quottext&quot class=&quotmessage_form__input&quot placeholder=&quotType a message&quot /> <button class=&quotmessage_form__button&quot type=&quotsubmit&quot> Enter </button> </form> </div> <script src=&quothttps://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js&quot> <script src=&quotmain.js&quot> </body> </html>سپس نیاز است که اتصال را در سمت فرانت‌اند راه‌اندازی کنید. تنها خط کدی که در فایل main.js نیاز دارید، const socket = io() است. در ویدیو زیر این موضوع را مشاهده خواهید کرد:همانطور که می‌بینید، هنگامی که صفحه باز و اسکریپت لود می‌شود، اتصال برقرار می‌شود. مورد بعدی که باید انجام دهید، مدیریت اتصال یک کاربر جدید، پیام جدید و کاربری که در حال تایپ کردن است، می‌باشد.در سوکت، بعد از اتصال کاربر، دو راه برای ایجاد eventها وجود دارد. راه اول از سمت کاربر به سمت دیگران (حتی خود کاربر) است و دیگری event را در سایر instanceها ایجاد می‌کند. ایده در اینجا، نمایش کاربران فعال است. بنابراین هنگامی که یک کاربر متصل می‌شود، آن‌ها باید در رابطه با آن اطلاعاتی کسب و لیست کاربران فعال در آن لحظه را دریافت کنند.const activeUsers = new Set(); io.on(&quotconnection&quot, function (socket) { console.log(&quotMade socket connection&quot); socket.on(&quotnew user&quot, function (data) { socket.userId = data; activeUsers.add(data); io.emit(&quotnew user&quot, [...activeUsers]); }); socket.on(&quotdisconnect&quot, () => { activeUsers.delete(socket.userId); io.emit(&quotuser disconnected&quot, socket.userId); }); });هنگامی که یک کاربر متصل می‌شود، این اتصال، eventیی را اجرا می‌کند که شامل اطلاعاتی در رابطه با نام‌کاربری آن‌ها می‌شود. باید userId را در سوکت، تنظیم کنید، این مورد به هنگام پایان اتصال کاربر مورد نیاز خواهد بود. برای انجام آن نیاز است که نام‌کاربری را به یک Set که شامل نام‌کاربری کاربران فعال است، اضافه کنید و در آخر event را به همراه لیستی از کاربران فعال، صدا بزنید.در فایل main.js، دو تابع و دو socket listener ایجاد کرده‌ایم:const socket = io(); const inboxPeople = document.querySelector(&quot.inbox__people&quot); let userName = &quot" const newUserConnected = (user) => { userName = user || `User${Math.floor(Math.random() * 1000000)}`; socket.emit(&quotnew user&quot, userName); addToUsersBox(userName); }; const addToUsersBox = (userName) => { if (!!document.querySelector(`.${userName}-userlist`)) { return; } const userBox = ` <div class=&quotchat_ib ${userName}-userlist&quot> <h5>${userName}</h5> </div> `; inboxPeople += userBox; }; // new user is created so we generate nickname and emit event newUserConnected(); socket.on(&quotnew user&quot, function (data) { data.map((user) => addToUsersBox(user)); }); socket.on(&quotuser disconnected&quot, function (userName) { document.querySelector(`.${userName}-userlist`).remove(); });ابتدا یک کاربر جدید ایجاد کردیم (به سادگی می‌توانید کارایی آن را با افزودن چندین ورودی از نام کاربران واقعی، گسترش دهید) و event را به همراه آن نام‌کاربری اجرا کردیم. هنگامی که اتصال یک کاربر در سمت سرور قطع می‌شود، نام‌کاربری آن از لیست کاربران فعال حذف و event مربوط به قطع اتصال یک کاربر به همراه نام آن کاربر، اجرا خواهد شد. بعد از آن، از لیست کاربران در سمت کلاینت حذف خواهد شد. در ویدیو زیر می‌توانید این مورد را مشاهده کنید:بعد از حل مسئله اتصال و قطع‌شدن کاربران، نوبت به مدیریت پیام‌های جدید می‌رسد. در سمت سرور، کاری به جز افزودن listener زیر، نیاز ندارید:socket.on(&quotchat message&quot, function (data) { io.emit(&quotchat message&quot, data); });در سمت کلاینت نیاز است موارد زیر را اضافه کنید:const inputField = document.querySelector(&quot.message_form__input&quot); const messageForm = document.querySelector(&quot.message_form&quot); const messageBox = document.querySelector(&quot.messages__history&quot); const addNewMessage = ({ user, message }) => { const time = new Date(); const formattedTime = time.toLocaleString(&quoten-US&quot, { hour: &quotnumeric&quot, minute: &quotnumeric&quot }); const receivedMsg = ` <div class=&quotincoming__message&quot> <div class=&quotreceived__message&quot> <p>${message}</p> <div class=&quotmessage__info&quot> <span class=&quotmessage__author&quot>${user}</span> <span class=&quottime_date&quot>${formattedTime}</span> </div> </div> </div>`; const myMsg = ` <div class=&quotoutgoing__message&quot> <div class=&quotsent__message&quot> <p>${message}</p> <div class=&quotmessage__info&quot> <span class=&quottime_date&quot>${formattedTime}</span> </div> </div> </div>`; messageBox += user === userName ? myMsg : receivedMsg; }; messageForm.addEventListener(&quotsubmit&quot, (e) => { e.preventDefault(); if (!inputField.value) { return; } socket.emit(&quotchat message&quot, { message: inputField.value, nick: userName, }); inputField.value = &quot" }); socket.on(&quotchat message&quot, function (data) { addNewMessage({ user: data.nick, message: data.message }); });تابع مسئول نمایش پیام جدید، بلافاصله بعد از دریافت اطلاعات از سوکت است. پیام توسط فرم، به سمت listener موجود در سمت سرور ارسال می‌شود. تنها نیاز است که چک کنید کلاینت نویسنده پیام است یا نه.اکنون به شما چگونگی اجرای eventهای مسئول برای سایر اتصالات، به غیر از اتصال خودتان را نمایش خواهیم داد. می‌خواهیم نشان دهیم که در این لحظه، یک کاربر در حال نوشتن چیزی است یا خیر. در سمت سرور، نیاز دارید تا socket.broadcast.emit را اضافه کنید. به کد زیر، که کد نهایی است، نگاهی بیندازید:const express = require(&quotexpress&quot); const socket = require(&quotsocket.io&quot); // App setup const PORT = 5000; const app = express(); const server = app.listen(PORT, function () { console.log(`Listening on port ${PORT}`); console.log(`http://localhost:${PORT}`); }); // Static files app.use(express.static(&quotpublic&quot)); // Socket setup const io = socket(server); const activeUsers = new Set(); io.on(&quotconnection&quot, function (socket) { console.log(&quotMade socket connection&quot); socket.on(&quotnew user&quot, function (data) { socket.userId = data; activeUsers.add(data); io.emit(&quotnew user&quot, [...activeUsers]); }); socket.on(&quotdisconnect&quot, () => { activeUsers.delete(socket.userId); io.emit(&quotuser disconnected&quot, socket.userId); }); socket.on(&quotchat message&quot, function (data) { io.emit(&quotchat message&quot, data); }); socket.on(&quottyping&quot, function (data) { socket.broadcast.emit(&quottyping&quot, data); }); });در سمت کلاینت، باید به event مرتبط به فشردن کلید برای inputField و socket listener برای typing، نگاهی بیندازید. در زیر هر دو مورد را در نسخه نهایی فایل main.js مشاهده می‌کنید:const socket = io(); const inboxPeople = document.querySelector(&quot.inbox__people&quot); const inputField = document.querySelector(&quot.message_form__input&quot); const messageForm = document.querySelector(&quot.message_form&quot); const messageBox = document.querySelector(&quot.messages__history&quot); const fallback = document.querySelector(&quot.fallback&quot); let userName = &quot" const newUserConnected = (user) => { userName = user || `User${Math.floor(Math.random() * 1000000)}`; socket.emit(&quotnew user&quot, userName); addToUsersBox(userName); }; const addToUsersBox = (userName) => { if (!!document.querySelector(`.${userName}-userlist`)) { return; } const userBox = ` <div class=&quotchat_ib ${userName}-userlist&quot> <h5>${userName}</h5> </div> `; inboxPeople += userBox; }; const addNewMessage = ({ user, message }) => { const time = new Date(); const formattedTime = time.toLocaleString(&quoten-US&quot, { hour: &quotnumeric&quot, minute: &quotnumeric&quot }); const receivedMsg = ` <div class=&quotincoming__message&quot> <div class=&quotreceived__message&quot> <p>${message}</p> <div class=&quotmessage__info&quot> <span class=&quotmessage__author&quot>${user}</span> <span class=&quottime_date&quot>${formattedTime}</span> </div> </div> </div>`; const myMsg = ` <div class=&quotoutgoing__message&quot> <div class=&quotsent__message&quot> <p>${message}</p> <div class=&quotmessage__info&quot> <span class=&quottime_date&quot>${formattedTime}</span> </div> </div> </div>`; messageBox += user === userName ? myMsg : receivedMsg; }; // new user is created so we generate nickname and emit event newUserConnected(); messageForm.addEventListener(&quotsubmit&quot, (e) => { e.preventDefault(); if (!inputField.value) { return; } socket.emit(&quotchat message&quot, { message: inputField.value, nick: userName, }); inputField.value = &quot" }); inputField.addEventListener(&quotkeyup&quot, () => { socket.emit(&quottyping&quot, { isTyping: inputField.value.length > 0, nick: userName, }); }); socket.on(&quotnew user&quot, function (data) { data.map((user) => addToUsersBox(user)); }); socket.on(&quotuser disconnected&quot, function (userName) { document.querySelector(`.${userName}-userlist`).remove(); }); socket.on(&quotchat message&quot, function (data) { addNewMessage({ user: data.nick, message: data.message }); }); socket.on(&quottyping&quot, function (data) { const { isTyping, nick } = data; if (!isTyping) { fallback = &quot" return; } fallback = `<p>${nick} is typing...</p>`; });عملکرد این مورد را می‌توانید در ویدیو زیر مشاهده کنید:جمع‌بندیامیدواریم این آموزش به شما در فهم سادگی استفاده از Socket.io کمکی کرده باشد، همچنین توسط دانشی پایه‌ای در رابطه با جاوااسکریپت و NodeJS می‌توانید کارهای جذاب زیادی انجام دهید. لیست زیر، برنامه‌هایی هستند که می‌توان توسط WebSockets، آن‌ها را ایجاد کرد:برنامه‌ای برای استریم مسابقات تیم ورزشی محل‌تان. می‌توانید بازی‌ها و یا ویدیوها یا حتی هر دو را استریم کنید.بازی چندنفره برای شما و دوستان‌تان.برنامه چت محرمانه برای گروه‌تان.برنامه‌های براساس GPS، شاید بازی‌هایی که از لوکیشن کاربر استفاده می‌کنند.به هنگام استفاده از Socket.io، تنها محدودیت، تصورات و ابتکارات شما است.بازنشر شده مقاله آموزش Socket.io از وبلاگ سرویس ابری لیارا