داکیومنت پیاده‌سازی WebSocket

برای اپلیکیشن‌های اندروید و iOS جهت کنترل تبلت سفارشی

فهرست مطالب

۱. معرفی و مقدمه

این داکیومنت، راهنمای جامعی برای پیاده‌سازی ارتباط WebSocket بین اپلیکیشن موبایل (اندروید و iOS) و تبلت سفارشی را ارائه می‌دهد. با استفاده از این راهنما، برنامه‌نویسان قادر خواهند بود یک سیستم ارتباطی پایدار، امن و سریع بین این دو دستگاه از طریق سرور WebSocket پیاده‌سازی کنند.

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

۲. معماری سیستم

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

  1. تبلت سفارشی: دستگاه اصلی که به سیستم‌های کنترلی متصل است و با شناسه UUID منحصر به فرد مشخص می‌شود.
  2. اپلیکیشن موبایل: برنامه‌ی نصب شده روی گوشی هوشمند کاربر که با deviceType: '1' شناخته می‌شود و دارای شناسه UUID منحصر به فرد است.
  3. سرور WebSocket: واسط بین اپلیکیشن موبایل و تبلت سفارشی که وظیفه انتقال پیام‌ها را به عهده دارد.

نکته مهم: سرور WebSocket صرفاً نقش واسط ارتباطی دارد و پیام‌ها را بدون تغییر از منبع به مقصد منتقل می‌کند. منطق کنترلی و پردازش اصلی در تبلت سفارشی و اپلیکیشن موبایل پیاده‌سازی می‌شود.

۳. ساختار پیام‌های WebSocket

تمام پیام‌های ارسالی و دریافتی با فرمت JSON انجام می‌شود و شامل فیلدهای زیر است:

فیلد توضیح مثال
code کد پیام که نوع عملیات را مشخص می‌کند '101'، '201'، '505'، '104'
sequence شماره ترتیب پیام که در هر درخواست جدید افزایش می‌یابد 0، 1، 2، ...
sourceId شناسه فرستنده پیام '5f3c6d61-434e-f64a-fcfa-b645a5637386'
targetId شناسه گیرنده پیام '688d2452-cf85-d16b-a094-c57e8b81347b'
time زمان ارسال پیام (میلی‌ثانیه) 1503
type نوع پیام 'com.astrum.websocket.JSONRequest'
deviceType نوع دستگاه (برای موبایل: '1') '1'
deviceName نام دستگاه 'M2101K6G-Xiaomi'
connectType نوع اتصال 3
ipSrc آدرس IP مبدا '2.144.3.143'
iptrg آدرس IP مقصد '2.144.3.143'
message محتوای اصلی پیام (در صورت نیاز) آبجکت JSON

مثال یک پیام درخواست از اپ موبایل به تبلت:

{
  "deviceType": "1",
  "sequence": 0,
  "code": "101",
  "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "time": 1503,
  "type": "com.astrum.websocket.JSONRequest",
  "deviceName": "M2101K6G-Xiaomi",
  "connectType": 3,
  "ipSrc": "2.144.3.143",
  "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "iptrg": "2.144.3.143"
}

مثال پیام پاسخ از تبلت به اپ موبایل:

{
  "targetId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "time": 8305688,
  "type": "com.lotus.websocket.JSONResponse",
  "code": "505",
  "sequence": 835,
  "connectType": 1,
  "ipSrc": "2.144.3.143",
  "sourceId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "iptrg": "2.144.3.143"
}

۴. انواع پیام‌ها و کدهای آنها

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

کد نوع پیام توضیح
100 مانیتورینگ-داده ارسال وضعیت مقدار یک کنترل یا پورت (مثل دما، روشن/خاموش، فعال/غیرفعال) توسط تبلت سفارشی
101 درخواست ثبت مانیتورینگ درخواست برقراری ارتباط با تبلت یا درخواست دریافت کل مقادیر برای کنترل‌ها و پورت‌ها
102 درخواست لغو مانیتورینگ درخواست برای لغو مانیتورینگ در وضعیت ثبت نشده
103 بررسی درخواست هدف درخواست بررسی هدف یا اطمینان از اتصال برای دریافت پاسخ 203 با پیام ready
104 نوشتن وضعیت درخواست اپ برای تغییر مقدار یک کنترل یا یک پورت
201 پاسخ ثبت دستگاه پاسخ تبلت برای تایید دسترسی در مورد درخواست 101 و ارسال کلی وضعیت مقادیر کنترل‌ها و پورت‌ها
202 پاسخ لغو مانیتورینگ پاسخ درخواست 102 در وضعیت ثبت نشده
203 بررسی پاسخ هدف پیام تبلت برای اعلام در حال مطالعه و تهیه خروجی پاسخ 201 یا پاسخ به درخواست 103
303 درخواست داده ارسال درخواست کوئری برای استخراج از دیتابیس از سمت اپ موبایل
304 پاسخ داده پاسخ درخواست 303 ارسالی توسط تبلت سفارشی
404 قطع ارتباط اعلام قطع ارتباط
503 رد دسترسی دسترسی رد شده توسط کاربر
505 در انتظار مجوز پاسخ تبلت برای اعلام انتظار تا تایید یا رد مجوز برای درخواست 101
1100 پیام VOIP ارتباط صوتی
1101 پیام دوربین ارتباط تصویری

هشدار: همه کدهای پیام به صورت رشته (String) ارسال می‌شوند، نه به صورت عدد. مثلاً "101" به جای 101.

۵. فرایند احراز هویت و اتصال

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

  1. شروع اتصال: اپ موبایل پیامی با کد '101' (درخواست ثبت مانیتورینگ) به سرور WebSocket ارسال می‌کند.
  2. انتظار برای تایید: تبلت سفارشی پاسخی با کد '505' (در انتظار مجوز) برای اپ موبایل می‌فرستد.
  3. تکرار: اپ موبایل مجدداً درخواست '101' را ارسال کرده و این چرخه تا تایید یا رد قطعی توسط کاربر تبلت ادامه می‌یابد.
  4. تایید دسترسی: پس از تایید توسط کاربر تبلت، پیامی با کد '201' (پاسخ ثبت) همراه با لیست کامل آیتم‌ها و مقادیر آنها به اپ ارسال می‌شود.
  5. رد دسترسی: در صورت عدم تایید، پیامی با کد '503' (رد دسترسی) به اپ ارسال می‌شود.
  6. بررسی اتصال: پس از تایید دسترسی، اپ موبایل به صورت دوره‌ای پیام‌هایی با کد '103' (بررسی درخواست هدف) به تبلت ارسال می‌کند و تبلت با کد '203' (پاسخ ready) پاسخ می‌دهد.

مثال روند احراز هویت:

  1. درخواست ثبت مانیتورینگ از اپ موبایل:

    {
      "deviceType": "1",
      "sequence": 0,
      "code": "101",
      "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "time": 1503,
      "type": "com.astrum.websocket.JSONRequest",
      "deviceName": "M2101K6G-Xiaomi",
      "connectType": 3,
      "ipSrc": "2.144.3.143",
      "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "iptrg": "2.144.3.143"
    }
  2. پاسخ انتظار مجوز از تبلت سفارشی:

    {
      "targetId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "time": 8305688,
      "type": "com.lotus.websocket.JSONResponse",
      "code": "505",
      "sequence": 835,
      "connectType": 1,
      "ipSrc": "2.144.3.143",
      "sourceId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "iptrg": "2.144.3.143"
    }
  3. تایید دسترسی و ارسال اطلاعات ایتم‌ها از تبلت:

    {
      "targetId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "time": 10308775,
      "message": [
        { "id": "5f8396c6-369d-45bd-ac3b-02ea27494682", "value": null },
        { "id": "be0727b7-6cca-4b5e-8b9f-881a312e9355", "value": null },
        { "id": "237697c2-9815-4b8e-b145-9b9798214390", "value": "1" },
        // صدها مورد دیگر
      ],
      "deviceInfo": {
        "deviceId": "L000A0924303B9",
        "screensaver_timeout": 60,
        "soundLevel": 50,
        // اطلاعات دستگاه
      },
      "type": "com.lotus.websocket.JSONResponse",
      "sequence": 856,
      "code": "201",
      "connectType": 1,
      "ipSrc": "2.144.3.143",
      "sourceId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "iptrg": "2.144.3.143"
    }

نکته: در صورت عدم پاسخ از سوی کاربر، سیستم تا حداکثر 15 بار پیام را با sequence متفاوت تکرار می‌کند. پس از آن اپلیکیشن باید مجدداً درخواست را ارسال کند.

۶. کنترل آیتم‌های مختلف

پس از برقراری ارتباط، اپلیکیشن موبایل می‌تواند آیتم‌های مختلف را کنترل کند. این کنترل از طریق ارسال پیام با کد '104' (نوشتن وضعیت) انجام می‌شود و تبلت وضعیت به‌روزرسانی شده را با کد '100' (مانیتورینگ-داده) باز می‌گرداند.

انواع آیتم‌های قابل کنترل:

نوع آیتم نوع داده (type) مقدار (value)
سوییچ/کلید INTEGER '0' (خاموش)، '1' (روشن)
لوستر INTEGER '0' (خاموش)، '1' (روشن)
شاتر INTEGER '0' (متوقف)، '1' (باز)، '2' (بسته)
دیمر INTEGER مقداری بین '0' تا '100' برای شدت نور
RGB کنترل INTEGER مقادیر رنگ (مثلا '32'، 'FF'، 'CCFFFFFF')
ترموستات - مد MODE 'Heat'، 'Cool'، 'Auto'، 'Frost_Protection'
ترموستات - فن FAN_SPEED 'Low'، 'Medium'، 'High'، 'Auto'
ترموستات - دما DOUBLE '1.0'، '24.5'، '35.23'

مثال‌های کنترل آیتم‌ها:

  1. روشن کردن یک کلید:

    {
      "sequence": 6,
      "code": "104",
      "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "time": 8088,
      "message": {
        "force": false,
        "id": "23ace3e0-92b8-43f0-8c49-6bf365fdede3",
        "type": "INTEGER",
        "value": "1"
      },
      "type": "com.astrum.websocket.JSONRequest",
      "connectType": 3,
      "ipSrc": "2.144.3.143",
      "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "iptrg": "2.144.3.143"
    }
  2. تغییر مود ترموستات:

    {
      "sequence": 223,
      "code": "104",
      "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "time": 8741,
      "message": {
        "force": false,
        "id": "c8b5a21f-44fc-4c41-a2b3-aa5ec39eb201",
        "type": "MODE",
        "value": "Frost_Protection"
      },
      "type": "com.astrum.websocket.JSONRequest",
      "connectType": 3,
      "ipSrc": "2.144.3.143",
      "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "iptrg": "2.144.3.143"
    }
  3. تغییر سرعت فن ترموستات:

    {
      "sequence": 378,
      "code": "104",
      "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
      "time": 9568,
      "message": {
        "force": false,
        "id": "6b37308e-b18d-4be7-8cc8-6b98c875f322",
        "type": "FAN_SPEED",
        "value": "High"
      },
      "type": "com.astrum.websocket.JSONRequest",
      "connectType": 3,
      "ipSrc": "2.144.3.143",
      "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
      "iptrg": "2.144.3.143"
    }

مثال پاسخ وضعیت (مانیتورینگ-داده):

{
  "targetId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "time": 10325345,
  "message": {
    "force": false,
    "id": "66047347-3b5b-4817-8713-a3bf8d1ab546",
    "value": "35.47"
  },
  "type": "com.lotus.websocket.JSONResponse",
  "sequence": 866,
  "code": "100",
  "connectType": 1,
  "ipSrc": "2.144.3.143",
  "sourceId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "iptrg": "2.144.3.143"
}

۷. دریافت اطلاعات از دیتابیس

اپلیکیشن موبایل می‌تواند با ارسال پیام با کد '303' (درخواست داده)، کوئری‌هایی را برای استخراج اطلاعات از دیتابیس تبلت ارسال کند. تبلت سفارشی نتیجه کوئری را با کد '304' (پاسخ داده) بازگرداند.

مثال ارسال کوئری برای دریافت دسترسی‌ها:

{
  "sequence": 2,
  "code": "303",
  "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "time": 4638,
  "message": { "queryLocal": "select * from tb_permission WHERE push_token = ''" },
  "type": "com.astrum.websocket.JSONRequest",
  "sequenceId": 1,
  "connectType": 3,
  "ipSrc": "2.144.3.143",
  "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "iptrg": "2.144.3.143"
}

مثال پاسخ کوئری:

{
  "targetId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "sequenceId": 1,
  "time": 10309477,
  "message": { "sequenceId": 1, "queryResult": [ {...} ] },
  "type": "com.lotus.websocket.JSONResponse",
  "sequence": 857,
  "code": "304",
  "connectType": 1,
  "ipSrc": "2.144.3.143",
  "sourceId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "iptrg": "2.144.3.143"
}

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

{
  "sequence": 3,
  "code": "303",
  "targetId": "688d2452-cf85-d16b-a094-c57e8b81347b",
  "time": 4834,
  "message": { "queryLocal": "select * from tb_scenarios" },
  "type": "com.astrum.websocket.JSONRequest",
  "sequenceId": 2,
  "connectType": 3,
  "ipSrc": "2.144.3.143",
  "sourceId": "5f3c6d61-434e-f64a-fcfa-b645a5637386",
  "iptrg": "2.144.3.143"
}

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

۸. ملاحظات امنیتی

برای حفظ امنیت ارتباط بین اپلیکیشن موبایل و تبلت سفارشی، موارد زیر را در نظر بگیرید:

  1. احراز هویت: همیشه از فرایند احراز هویت کامل (کدهای 101، 505، 201، 503) استفاده کنید.
  2. بررسی ID: همواره از ID معتبر و شناخته‌شده برای sourceId و targetId استفاده کنید.
  3. تایید کاربر: همیشه تایید کاربر را قبل از شروع ارتباط دریافت کنید.
  4. کنترل دسترسی: تنها به آیتم‌هایی که کاربر به آنها دسترسی دارد اجازه دسترسی بدهید.
  5. بررسی توالی: شماره sequence را برای تشخیص پیام‌های تکراری یا نامرتب بررسی کنید.
  6. رمزنگاری: برای محیط‌های حساس، از ارتباط WSS (WebSocket Secure) به جای WS استفاده کنید.

هشدار امنیتی: هرگز کوئری‌های دیتابیس را بدون اعتبارسنجی و فیلتر کردن اجرا نکنید. این کار می‌تواند منجر به حملات SQL Injection شود.

۹. بهینه‌سازی عملکرد و پایداری

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

  1. مدیریت قطع ارتباط: سازوکاری برای تشخیص قطع ارتباط و اتصال مجدد خودکار پیاده‌سازی کنید.
  2. ذخیره‌سازی محلی: اطلاعات دریافت شده از تبلت را در دیتابیس محلی اپ ذخیره کنید تا در صورت قطعی موقت شبکه، عملکرد اپ حفظ شود.
  3. صف پیام‌ها: پیام‌های ارسالی را در یک صف قرار دهید تا در صورت قطع ارتباط و اتصال مجدد، از دست نروند.
  4. محدود کردن تکرار: برای پیام‌های تکراری، حداکثر تعداد تلاش تعیین کنید (مثلاً 15 بار).
  5. کنترل وضعیت اتصال: با ارسال منظم پیام‌های کد 103 (بررسی درخواست هدف)، از برقراری اتصال اطمینان حاصل کنید.
  6. مدیریت منابع: منابع WebSocket را به درستی آزاد کنید و از نشت حافظه جلوگیری نمایید.

نکته: برای کاهش بار شبکه، تنها تغییرات وضعیت را ارسال کنید، نه تمام آیتم‌ها را در هر به‌روزرسانی.

۱۰. نکات پیاده‌سازی در اندروید

برای پیاده‌سازی WebSocket در اندروید، می‌توانید از کتابخانه‌های زیر استفاده کنید:

نکات مهم پیاده‌سازی:

  1. مدیریت چرخه حیات: ارتباط WebSocket را در onResume() باز کنید و در onPause() یا onStop() ببندید.
  2. استفاده از سرویس: برای حفظ ارتباط در پس‌زمینه از یک سرویس (Service) استفاده کنید.
  3. اعلان‌ها: در صورت دریافت پیام‌های مهم در پس‌زمینه، از اعلان‌ها (Notifications) استفاده کنید.
  4. مدیریت شبکه: وضعیت شبکه را بررسی کنید و در صورت تغییر وضعیت (مثلاً از WiFi به داده موبایل)، ارتباط را مجدداً برقرار کنید.
  5. مدیریت حالت خواب: از WakeLock برای جلوگیری از قطع ارتباط در حالت خواب دستگاه استفاده کنید.

نکته: شماره sequence پیام‌ها باید به صورت تصاعدی افزایش یابد. این شماره را در حافظه نگه دارید و در هر بار ارسال پیام، آن را افزایش دهید.

۱۱. نکات پیاده‌سازی در iOS

برای پیاده‌سازی WebSocket در iOS، می‌توانید از کتابخانه‌های زیر استفاده کنید:

نکات مهم پیاده‌سازی:

  1. مدیریت چرخه حیات: ارتباط WebSocket را در viewDidAppear() باز کنید و در viewDidDisappear() ببندید.
  2. پردازش در پس‌زمینه: از URLSession با کانفیگ background برای حفظ ارتباط در پس‌زمینه استفاده کنید.
  3. مدیریت حالت اپ: در دلیگیت applicationDidEnterBackground و applicationWillEnterForeground ارتباط را به درستی مدیریت کنید.
  4. استفاده از GCD: از Grand Central Dispatch برای پردازش غیرهمزمان پیام‌ها استفاده کنید.
  5. ذخیره‌سازی محلی: از CoreData یا Realm برای ذخیره‌سازی محلی اطلاعات استفاده کنید.

نکته: از NSUserDefaults برای ذخیره deviceId و sourceId استفاده کنید تا در اجراهای بعدی اپلیکیشن، نیاز به ثبت مجدد نباشد.

۱۲. بهترین شیوه‌های پیاده‌سازی

برای پیاده‌سازی بهینه و کارآمد، موارد زیر را رعایت کنید:

  1. معماری مناسب: از معماری MVVM یا Clean Architecture برای تفکیک منطق کاری و ارتباط با WebSocket استفاده کنید.
  2. الگوهای طراحی: از الگوهایی مانند Observer، Command و Singleton برای مدیریت بهتر پیام‌ها و رویدادها استفاده کنید.
  3. مدیریت خطا: سیستم مدیریت خطای قوی برای پیام‌های نامعتبر، قطع ارتباط و تایم‌اوت پیاده‌سازی کنید.
  4. لاگینگ: سیستم لاگ مناسب برای ثبت رویدادها، خطاها و پیام‌های ارسالی و دریافتی پیاده‌سازی کنید.
  5. میانی‌افزار: برای پردازش پیام‌ها قبل از ارسال یا بعد از دریافت، از میانی‌افزار (Middleware) استفاده کنید.
  6. واکنش به رویداد: از الگوهای مبتنی بر رویداد مانند RxJava یا Combine برای واکنش به تغییرات وضعیت استفاده کنید.

هشدار: هرگز اطلاعات حساس را به صورت متن ساده (plain text) از طریق WebSocket ارسال نکنید. در صورت نیاز از رمزنگاری سمت کلاینت استفاده کنید.

۱۳. عیب‌یابی و رفع خطاها

خطاهای رایج و راه‌حل‌های آنها:

مشکل علت احتمالی راه حل
عدم برقراری ارتباط آدرس نادرست یا مشکل شبکه بررسی آدرس سرور WebSocket و وضعیت شبکه
قطع مکرر ارتباط مشکل شبکه یا تایم‌اوت اتصال مجدد خودکار و افزایش زمان تایم‌اوت
عدم دریافت پاسخ مشکل در ساختار پیام یا شناسه‌ها بررسی صحت sourceId، targetId و ساختار پیام
خطای دسترسی عدم تایید کاربر یا منقضی شدن مجوز ارسال مجدد درخواست ثبت (کد 101)
تاخیر در دریافت پاسخ کندی شبکه یا پردازش سنگین استفاده از تایم‌اوت و مدیریت حالت‌های انتظار
ناسازگاری در وضعیت آیتم‌ها از دست رفتن پیام‌ها یا تاخیر ارسال درخواست بروزرسانی کلی وضعیت (کد 101)

روند عیب‌یابی:

  1. ثبت لاگ: پیام‌های ارسالی و دریافتی را به طور کامل ثبت کنید.
  2. بررسی sequence: شماره‌های sequence را برای کشف پیام‌های گمشده بررسی کنید.
  3. تست اتصال: با ارسال پیام کد 103، وضعیت اتصال را بررسی کنید.
  4. مقایسه وضعیت: وضعیت محلی و وضعیت دریافتی از تبلت را مقایسه کنید.
  5. تست ارتباط مجدد: اتصال را قطع کرده و بررسی کنید آیا اتصال مجدد به درستی انجام می‌شود.

نکته: از ابزارهایی مانند Wireshark یا Charles Proxy برای نظارت بر ترافیک WebSocket و عیب‌یابی دقیق‌تر استفاده کنید.