Node.js یک محیط اجرای جاوا اسکریپت اوپن‌سورس محبوب است که بر مبنای موتور جاوا اسکریپت V8 کروم ساخته شده است. Node.js برای ساخت برنامه‌های سمت سرور و شبکه‌ای استفاده می‌شود. TCP (پروتکل کنترل انتقال) یک پروتکل شبکه است که امکان ارسال پایدار جریان داده‌ای، مرتب و با بررسی خطا را بین اپلیکیشن‌های مختلف ایجاد کرده است. یک سرور TCP می‌تواند درخواست اتصال TCP را بپذیرد و زمانی که اتصال بین دو طرف برقرار شد، جریان‌های داده را مبادله کند.

در این راهنما یک سرور TCP مبتنی بر Node.js همراه با یک کلاینت برای تست سرور می‌سازیم. سروری که ساخته‌ایم را به وسیله یک نرم‌افزار مدیریت پروسه قدرتمند به نام PM2 به عنوان یک پروسه پس‌زمینه اجرا می‌کنیم. سپس انجین‌ایکس را به عنوان یک پراکسی معکوس برای اپلیکیشن TCP پیکربندی می‌کنیم و اتصال سمت کلاینت را از روی رایانه محلی تست می‌کنیم.

پیش‌نیازها

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

  • یک سرور اوبونتو 16.04 که دارای یک کاربر غیر root با دسترسی sudo و فایروال پیکربندی شده باشد.
  • Nginx را بر روی سرور خود نصب می‌کنیم. این نصب باید همراه با گزینه with-stream– باشد که در نصب‌های اولیه انجین‌ایکس از طریق نرم‌افزار مدیریت بسته apt در اوبونتو 16.04 به طور پیش‌فرض وجود دارد.
  • Node.js را با استفاده از PPA رسمی نصب می‌کنیم.

گام 1: ایجاد یک اپلیکیشن TCP مبتنی بر Node.js

ما یک اپلیکیشن Node.js را با استفاده از سوکت‌های TCP می‌نویسیم. این یک برنامه ساده است که به شما کمک می‌کند تا کتابخانه Net را در Node.js بهتر درک کنید و بتوانید اپلیکیشن‌های سرور و کلاینت TCP را بنویسید.

برای شروع یک دایرکتوری روی سروری که می‌خواهید اپلیکیشن خود را قرار دهید ایجاد کنید. در این راهنما ما اپلیکیشن خود را در دایرکتوری tcp-nodejs-app/~ قرار می‌دهیم:

سپس به دایرکتوری جدید بروید:

یک فایل به نام package.json برای پروژه خود انتخاب کنید. در این فایل همه بسته‌هایی که اپلیکیشن نیاز دارد فهرست شده‌اند. ایجاد این فایل باعث می‌شود که build شما قابل تولید مجدد باشد و بدین ترتیب اشتراک این فهرست از وابستگی‌ها با توسعه‌دهندگان دیگر آسان‌تر خواهد بود:

همچنین می‌توانید بسته package.json را با استفاده از دستور npm init ایجاد کنید که از شما می‌خواهد جزییات اپلیکیشن را ذکر کنید. با این وجود همچنان مجبور هستیم در ادامه فایل را به صورت دستی ویرایش کنیم تا امکان افزودن تکه‌های دیگر شامل دستورهای راه‌اندازی اولیه را داشته باشیم. از این رو در این راهنما، این فایل را به طور دستی ایجاد می‌کنیم.

کد JSON زیر را که نام اپلیکیشن، نسخه، فایل اصلی (main)، دستور آغاز برنامه و لایسنس اپلیکیشن را در خود جای داده، به فایل اضافه کنید:

در فیلد scripts می‌توانید دستوراتی برای اپلیکیشن تعریف کنید. تنظیماتی که در اینجا تعیین می‌کنیم به ما امکان می‌دهد که به جای اجرای دستور node server.js بتوانیم با اجرای دستور npm start برنامه خود را اجرا کنیم.

فایل package.json می‌تواند شامل فهرستی از وابستگی‌های زمان اجرا و توسعه نیز باشد؛ اما هیچ وابستگی شخص ثالث برای این اپلیکیشن وجود ندارد.

اینک که دایرکتوری پروژه و فایل package.json را تنظیم کردیم، می‌توانیم به مراحل ساخت سرور بپردازیم. در دایرکتوری اپلیکیشن یک فایل به نام server.js بسازید:

Node.js ماژولی به نام net دارد که امکان ارتباط بین سرور و اپلیکیشن را با پروتکل TCP ایجاد می‌کند. ماژول net را با دستور ()require بارگذاری کنید و سپس متغیرهایی که پورت و هاست سرور را تعیین می‌کنند، تعریف نمایید:

ما در این برنامه از پورت 7070 استفاده می‌کنیم؛ اما شما می‌توانید هر پورتی که در دسترس است را تعیین کنید. همچنین در این فایل از آی‌پی 127.0.0.1 برای متغیر HOST استفاده کرده‌ایم تا مطمئن باشیم که سرور ما تنها به رابط شبکه محلی گوش می‌دهد. در ادامه Nginx را در جلوی این اپلیکیشن قرار می‌دهیم تا به عنوان یک پراکسی معکوس عمل کند. Nginx عملکرد مناسبی در مدیریت اتصال‌های چندگانه و مقیاس‌بندی افقی دارد.

سپس کد زیرا به فایل اضافه می‌کنیم تا یک سرور TCP را با استفاده از تابع ()createServer از ماژول net بسازیم. در این مرحله با استفاده از تابع ()listen در ماژول net می‌توانیم از سرور بخواهیم که درهاست و پورت تعریف شده شروع به گوش دادن اتصال‌های ورودی بکند.

فایل server.js را ذخیره کرده و سرور را آغاز کنید:

خروجی زیرا مشاهده می‌کنید:

سرور TCP بر روی پورت 7070 فعال شده است. با زدن دکمه‌های CTRL+C سرور را متوقف کنید. اینک که می‌دانیم سرور در حال گوش دادن است، می‌توانیم کدی را بنویسیم تا اتصال‌ها را مدیریت کنیم.

زمانی که یک کلاینت به سرور متصل می‌شود، این سرور یک رویداد connection را فعال می‌کند که در ادامه بیشتر در موردش صحبت می‌کنیم. در این مرحله یک آرایه از کلاینت‌های اتصال یافته تعریف می‌کنیم که آن را sockets نامگذاری می‌کنیم و هر وهله از کلاینت که به سرور وصل می‌شود را به این آرایه اضافه می‌کنیم.

ما از رویداد data برای پردازش جریان داده‌ای از کلاینت‌های اتصال یافته استفاده می‌کنیم و از آرایه sockets برای انتشار داده‌ها به همه کلاینت‌های اتصال یافته بهره می‌گیریم.

کد زیر را به فایل server.js اضافه کنید تا این ویژگی‌ها را پیاده‌سازی کند:

کد فوق به سرور می‌گوید که به رویدادهای data ارسالی از سوی کلاینت‌های اتصال یافته گوش دهد. زمانی که کلاینتِ متصل، هر نوع از داده را به سرور ارسال می‌کند، ما آن را از طریق تعریف حلقه‌ای روی آرایه sockets به همه کلاینت‌های اتصال یافته باز می‌گردانیم.

سپس یک دستگیره (handler) برای رویدادهای close که هنگام خاتمه یافتن اتصال کلاینت فعال می‌شوند، ایجاد می‌کنیم. هر زمان که یک کلاینت اتصال خود را قطع کند، باید آن کلاینت را از آرایه sockets حذف کنیم تا دیگر پیام‌ها به آن کلاینت ارسال نشوند. کد زیر را به انتهای بلوک اتصال اضافه کنید:

اینک کد کامل فایل سرور ما باید به صورت زیر درآمده باشد:

فایل را ذخیره کنید و سرور را مجدداً آغاز کنید:

اینک یک سرور TCP کاملاً فعال روی رایانه سرور خود داریم. در مرحله بعد باید یک کلاینت ایجاد کنیم تا به این سرور ما وصل شود.

گام 2: ایجاد کلاینت TCP مبتنی بر Node.js

در حال حاضر سرور TCP روی Node.js مشغول اجرا است و از این رو باید یک کلاینت TCP ایجاد کنیم تا به سرور وصل شود و بتوانیم سرور خود را تست کنیم.

سرور Node.js که در مرحله قبل نوشتیم، همچنان در حال اجرا است و جلسه ترمینال کنونی شما را مسدود ساخته است. از آنجا که می‌خواهیم این سرور همچنان در طی مراحل توسعه کلاینت نیز در حال اجرا باقی بماند، باید یک پنجره ترمینال جدید باز کنیم. سپس از این ترمینال جدید دوباره به سرور خود وصل می‌شویم:

زمانی که وصل شدیم به دایرکتوری tcp-nodejs-app می‌رویم:

در این دایرکتوری یک فایل جدید به نام client.js می‌سازیم:

این کلاینت از همان کتابخانه net که در فایل server.js استفاده کردیم، بهره می‌گیرد تا به سرور TCP وصل شود. کد زیر را به فایل اضافه کنید تا با استفاده از آدرس آی‌پی 127.0.0.1 و شماره پورت 7070 به سرور وصل شوید:

کد فوق تلاش می‌کند تا به سرور TCP وصل شود و بدین ترتیب اطمینان حاصل شود که سروری که ایجاد کرده‌ایم در حال اجرا است. زمانی که اتصال برقرار شد، کلاینت یک پیام “Hello From Client ” + client.address().address را با استفاده از تابع client.write به سرور می‌فرستد. سرور ما این داده‌ها را دریافت می‌کند و آن را به کلاینت باز می‌گرداند.

زمانی که کلاینت داده‌ها را از سرور بازدریافت می‌کند، پاسخ سرور را نمایش می‌دهیم. کد زیر را به رویداد data اضافه کنید تا پاسخ سرور را در خط فرمان نمایش دهد:

در نهایت وضعیت قطع اتصال از سرور را با افزودن کد زیر به طور مناسبی مدیریت می‌کنیم:

فایل client.js را ذخیره کنید و دستور زیرا برای آغاز به کار کلاینت وارد نمایید:

اتصال برقرار می‌شود و سرور داده‌ها را دریافت کرده و آن‌ها را مجدداً به کلاینت ارسال می‌کند:

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

بدین ترتیب تأیید شد که اتصال TCP بین برنامه‌های سرور و کلاینت برقرار شده است.

CTRL+C را بزنید تا سرور متوقف شود. سپس به ترمینال دیگر رفته و مجدداً CTRL+C را بزنید تا کلاینت نیز متوقف شود. اینک می‌توانید این ترمینال را از سرور قطع کنید و به ترمینال اول بازگردید. در مرحله بعد سرور را با استفاده از PM2 آغاز و آن را در پس‌زمینه اجرا می‌کنیم.

گام 3: اجرای سرور با PM2

اینک یک سرور فعال داریم که اتصال‌ها را از سمت کلاینت‌ها می‌پذیرد؛ اما این سرور در پیش‌زمینه اجرا می‌شود. می‌توانیم با استفاده از PM2 سرور را در پس‌زمینه اجرا کنیم و آن را به طور مناسبی راه‌اندازی مجدد کنیم. بدین منظور ابتدا باید PM2 را با استفاده از دستور npm به طور سراسری روی سرور نصب کنید:

زمانی که PM2 نصب شد، از آن برای اجرای سرور خود استفاده کنید. به جای اجرای npm start برای شروع سرور می‌توانید از دستور pm2 استفاده کنید. سرور را آغاز کنید:

در این مرحله یک خروجی مانند زیر می‌بینید:

اینک سرور در پس‌زمینه اجرا شده است. با این حال اگر سرور را ریبوت (reboot) کنیم، این سرور به طور خودکار مجدداً آغاز به کار نمی‌کند. بنابراین باید یک سرویس systemd برای آن ایجاد کنیم.

دستور زیر را اجرا کنید تا اسکریپت‌های راه‌اندازی systemd را برای PM2 تولید و نصب کنید. مطمئن شوید که در هنگام اجرای دستور از sudo استفاده می‌کنید تا فایل‌ها به طور خودکار نصب شوند.

PM2 اینک به عنوان یک سرویس system اجرا شده است. شما می‌توانید همه پروسس‌های PM2 را با دستور pm2 list فهرست کنید:

اپلیکیشن خود را در این لیست با ID صفر می‌بینید:

در خروجی فوق می‌بینید که مقدار watching به صورت غیر فعال (disabled) است. این ویژگی موجب می‌شود که هر زمان شما تغییری در هر یک از فایل‌های اپلیکیشن ایجاد می‌کنید، سرور به طور خودکار راه‌اندازی مجدد شود. این وضعیت در مرحله توسعه مفید است؛ اما در محیط استفاده عملی (production) به آن نیازی نداریم.

برای کسب اطلاعات بیشتر در مورد هرگونه پروسه در حال اجرا، از دستور pm2 show و سپس ID آن استفاده می‌کنیم. در این مورد ID برابر با صفر است:

خروجی فوق میزان آپتایم، وضعیت سرور، مسیرهای فایل log و دیگر اطلاعات را در مورد اپلیکیشن در حال اجرا نشان می‌دهد:

اگر وضعیت اپلیکیشن خطایی را نشان دهد، می‌توانید از error lop path برای باز کردن و مرور لاگ‌های خطا جهت دیباگ آن استفاده کنید:

اگر تغییراتی در کد سرور ایجاد کرده باشید، باید پروسه اپلیکیشن را راه‌اندازی مجدد کنید تا تغییرات اعمال شوند، مثلاً به صورت زیر:

PM2 اینک در حال مدیریت اپلیکیشن است. در مرحله بعد از Nginx برای پراکسی کردن درخواست‌ها به سرور استفاده می‌کنیم.

گام 4: راه‌اندازی Nginx به عنوان یک سرور پراکسی معکوس

در مرحله کنونی اپلیکیشن شما در حال اجرا است و در آدرس 127.0.0.1 مشغول گوش دادن است. این به آن معنی است که این اپلیکیشن تنها اتصال‌هایی را از رایانه محلی خود دریافت می‌کند. ما Nginx را به عنوان یک پراکسی معکوس تنظیم می‌کنیم که ترافیک ورودی را مدیریت کرده و آن را به سرور ما هدایت می‌کند. برای این کار Nginx را جهت استفاده از ویژگی‌های ()stream و stream_proxy پیکربندی می‌کنیم تا داده‌ها را تنها به صورت بلوک‌های سطح بالا فوروارد کنیم. پیکربندی پیش‌فرض Nginx روی اوبونتو بلوک‌های سرور را درون بلوک http فایل بارگذاری می‌کند و بلوک stream نمی‌تواند درون این بلوک قرار گیرد.

فایل /etc/nginx/nginx.conf را درون ویرایشگر دلخواه خود باز کنید:

خطوط زیر را به انتهای فایل پیکربندی اضافه کنید:

این کد به اتصال‌های TCP روی پورت 3000 گوش می‌دهد و درخواست‌ها را به سرور Node.js که روی پورت 7070 در حال اجرا است پراکسی می‌کند. اگر اپلیکیشن طوری تنظیم شده باشد که به شماره پورت دیگری گوش دهد، پراکسی پورت URL را به شماره پورت صحیح ارسال می‌کند. مقدار proxy_protocol به انجین‌ایکس می‌گوید که از پروتکل PROXY برای ارسال اطلاعات کلاینت به سرورهای بک‌اند استفاده کند. این اطلاعات سپس در آنجا در صورت نیاز مورد پردازش قرار می‌گیرند. فایل را ذخیره کرده و از ویرایشگر خارج شوید.

پیکربندی Nginx را بررسی کنید تا مطمئن شوید که هیچ گونه خطای نحوی در فایل ایجاد نشده است:

سپس Nginx را رستارت کنید تا امکان پراکسی TCP و UDP فعال شود:

در این مرحله به اتصال‌های TCP اجازه می‌دهیم تا روی آن پورت به سرور ما وصل شوند. از ufw برای ایجاد اتصال به پورت 3000 استفاده می‌کنیم:

با فرض این که اپلیکیشن Node.js شما در حال اجرا است و اپلیکیشن و پیکربندی‌های Nginx شما صحیح هستند، اینک باید قادر باشید که از طریق پراکسی معکوس انجین‌ایکس به اپلیکیشن خود دسترسی پیدا کنید.

گام 5: تست اتصال سمت کلاینت

اجازه بدهید سرور خود را با بهره‌گیری از اسکریپت client.js و با اتصال به سرور TCP از رایانه محلی تست کنیم. بدین منظور باید فایل client.js را که توسعه داده‌ایم، بر روی رایانه محلی خود دانلود کنید و آدرس آی‌پی و شماره پورت را در اسکریپت تغییر دهید.

ابتدا روی رایانه محلی خود فایل client.js را با استفاده از دستور scp دانلود کنید:

شماره پورت را به 3000 تغییر دهید و مقدار host را برابر با آدرس IP سرور خود قرار دهید:

فایل را ذخیره کنید و از ویرایشگر خارج شوید. اینک می‌توانید همه چیز را با اجرای کلاینت تست کنید:

در این مرحله همان خروجی که قبلاً دیدیم را مجدداً مشاهده می‌کنید که نشان می‌دهد رایانه کلاینت از طریق Nginx به سرور دسترسی یافته است:

از آن جا که Nginx اتصال‌های کلاینت را به سرور شما پراکسی می‌کند، سرور Nginx شما آدرس واقعی آی‌پی کلاینت‌ها را مشاهده نمی‌کند و تنها آدرس آی‌پی Nginx را می‌بیند. Nginx بدون ایجاد برخی تغییرات از ارسال مستقیم آدرس‌های واقعی آی‌پی به بک‌اند پشتیبانی نمی‌کند. این تغییرات ممکن است بر روی امنیت سیستم تأثیر منفی بگذارند؛ اما از آنجا که پروتکل PROXY را در Nginx فعال کرده‌ایم، سرور Node.js اینک پیام‌های PROXY اضافی را نیز دریافت می‌کند که شامل آی‌پی واقعی هستند. اگر به این آدرس‌های آی‌پی نیاز دارید، می‌توانید سرور خود را تغییر دهید تا درخواست‌های PROXY را پردازش کرده و داده‌هایی که لازم دارید را تجزیه کند.

سخن پایانی

اینک شما یک اپلیکیشن TCP مبتنی بر Node.js دارید که در پشت یک پراکسی معکوس انجین‌ایکس فعالیت می‌کند و می‌توانید به کار خود ادامه داده و سرور را بیش از این توسعه دهید.

در این راهنما یک اپلیکیشن Node.js ایجاد کردیم که به همراه PM2 کار می‌کند و آن را در پشت یک وب‌سرور انجین‌ایکس قرار دادیم. همچنین یک اپلیکیشن کلاینت ساختیم که از روی رایانه‌های دیگر می‌تواند به سرور ما متصل شود. شما می‌توانید از این اپلیکیشن برای مدیریت حجم بالایی از جریان‌های داده‌ای و ساخت اپلیکیشن‌های پیام‌رسانی همزمان استفاده کنید.

اگر این نوشته مورد توجه شما قرار گرفته است، پیشنهاد می‌کنیم موارد زیر را نیز مورد مطالعه قرار دهید:

==

«میثم لطفی» دانش‌آموخته ریاضیات و شیفته فناوری به خصوص در حوزه رایانه است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار نیز با مجله فرادرس همکاری دارد.

بر اساس رای 2 نفر

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *