1. الاتفاق
WebSocket هو بروتوكول يعتمد على الاتصالات المزدوجة الكاملة بين العملاء والخوادم على TCP. يتم تعريفه في HTML5 وهو أيضًا أحد المواصفات الأساسية للجيل الجديد من WebApps.
إنه يخترق قيود Ajax السابقة ، والمفتاح هو في الوقت الفعلي ، ويمكن للخادم دفع المحتوى بنشاط إلى العميل! تتضمن التطبيقات الممكنة: الألعاب متعددة اللاعبين عبر الإنترنت ، والدردشة المباشرة ، والمراقبة في الوقت الفعلي ، وسطح المكتب البعيد ، وخادم الأخبار ، إلخ.
بالنسبة لنفسي ، فإن ما أريد تجربته في الوقت الحالي هو ما يمكن القيام به مع مجموعة Canvas+WebSocket.
2. الإدراك
نظرًا لأن عملية المصافحة هي طلب HTTP قياسي ، فهناك خياران لتنفيذ WebSocket: 1) تطبيق على TCP ؛ 2) التنفيذ على برنامج HTTP الحالي. الميزة الأخيرة هي أنه يمكنها مشاركة منافذ خادم HTTP الحالية ولا تحتاج إلى إعادة تنفيذ وظيفة المصادقة وطلبات HTTP.
يتم استخدام وحدة HTTP للعقدة في هذا المثال. (انظر المرفق لإصدار TCP وجميع الملفات)
1. رمز جانب خادم العقدة:
var http = require ('http') ؛ var url = require ('url') ؛ // var mime = require ('mime') ؛ var crypto = require ('crypto') ؛ var port = 4400 ؛ var server = http.createserver () ؛ server.listen (port ، function () {console.log ('server قيد التشغيل على localhost:' ، port) ؛ server .on ('connection' ، function (s) {console.log ('on connection' ، s) ؛}) .on ('request' ، onrequest) .on ('updgrade' ، onupgrade) ؛ Object.Keys (req) ، req.url ، req ['Upgrade']) ؛ if (! req.upgrade) {// اختيار طلب غير ترقية: مقاطعة أو توفير صفحة ويب عادية res.writehead (200 ، {'content-type': 'text/plain'}) ؛ Res.Write ('WebSocket Server Works!') ؛ } res.end () ؛ return ؛} ؛ var onupgrade = function (req ، sock ، head) {// console.log ('method:' ، object.keys (sock)) ؛ if (req.headers.upgrade! == 'WebSocket') {console.warn ('connection incalial') ؛ Sock.end () ؛ يعود؛ } bind_sock_event (sock) ؛ جرب {مصافحة (req ، جورب ، الرأس) ؛ } catch (e) {console.error (e) ؛ Sock.end () ؛ }} ؛ // لف الإطار المراد إرساله var wrap = function (data) {var fa = 0x00 ، fe = 0xff ، data = data.toString () len = 2+buffer.bytelength (data) ، buff = new buffer (len) ؛ Buff [0] = FA ؛ Buff.write (البيانات ، 1) ؛ برتقالي [len-1] = fe ؛ إرجاع Buff ؛} // قم بفك الإطار المستلم var var unf = function (data) {return data.slice (1 ، data.length.length-1) ؛} var bind_sock_event = function (sock) {sock .on ('data' ، function (buffer) {var data = unfer (buffer) ؛ Send ('Hello Html5 ،'+date.now ()) sock.emit ('send' ، data) ؛ Sock.Write (WRAP (DATA) ، 'binary') ؛ إذا كان (! المسافات) رمي {message: "مفتاح خاطئ: '+مفتاح ، الاسم:' المصافحة '} return get_big_endian (part / spaces) ؛} var get_big_endian = function (n) Function (key1 ، key2 ، head) {var sum = get_part (key1) + get_part (key2) + head.toString ('binary') ؛ إرجاع crypto.createhash ('md5'). تحديث (sum) .digest ('binary') ؛} var handshake = function (req ، sock ، head) {var output = [] ، h = req.headers ، br = '/r/n' ؛ // header output.push ('http/1.1 101 WebSocket Protocol Handshake' ، 'Upgrade: WebSocket' ، 'Connection: Upgrade' ، 'Sec-Websocket-Origin:' + h.origin ، 'sec-webtocooce: //' + h.host + req.url ، 'sec-webbtocol. // body var c = Challenge (h ['sec-websocket-key1'] ، h ['sec-websocket-key2'] ، head) ؛ output.push (c) ؛ Sock.write (output.join (br) ، 'binary') ؛}2. رمز عميل المتصفح:
<html> <Head> <title> WebSocket Demo </title> </head> <style type = "text/css"> textarea {width: 400px ؛ الارتفاع: 150px ؛ عرض: block ؛ verflow-y: scroll ؛} #output {width: 600px ؛ Quant: 400px ؛ probert. الزر {padding: .2em 1em ؛} </style> <link href = "lyout.css" rel = "stylesheet" type = "text/css"/> <body> <textarea id = "outputive" readonly = "readonly"> </spricea> <br> type = "text/javaScript"> // localhostvar socket = new websocket ('ws: //192.168.144.131: 4400/') socket.onopen = function (e) {log (e.type) ؛ socket.send ('hello node') ؛} socket.onclose = function (e) {log ( output.scrolltop = output.scrollheight} socket.onclose = function (e) {log ( معرف ('input') ، send = id ('send') ؛ var log = function (msg) {output.textContent += '>' +msg +'/n'} send.addeventListener ('click' ، function () {socket.send (input.value) ؛3. التفاصيل
يحتوي تطبيق بروتوكول WebSocket فوق بروتوكول HTTP على خطوتين فقط: المصافحة وإرسال البيانات.
1. مصافحة
تسمى عملية المصافحة استجابة التحدي. أولاً ، يبدأ العميل طلب HTTP GET المسمى الترقية ، ويتحقق الخادم من الطلب ، ويعطي 101 استجابة للإشارة إلى أن ترقية البروتوكول مقبولة ، ويتم اكتمال المصافحة.
معلومات المصافحة التي تم تجميلها بواسطة مفتش الكروم:
طلب عنوان URL: WS: //192.168.144.131: 4400/pub/chat؟ q = me
طريقة الطلب: احصل
رمز الحالة: 101 مصافحة بروتوكول WebSocket
طلب الرؤوس
الاتصال: الترقية
المضيف: 192.168.144.131: 4400
الأصل: http: // localhost: 800
Sec-Websocket-Key1: P2 G 947T 80 661 JAF2
sec-websocket-key2: z zq ^326 5 9 = 7S1 1 7H4
Sec-Websocket-Protocol :: My-Custom-Chat-Protocol
الترقية: WebSocket
(Key3): 7C: 44: 56: CA: 1F: 19: D2: 0A
رؤوس الاستجابة
الاتصال: الترقية
Sec-Websocket Location: WS: //192.168.144.131: 4400/pub/chat؟ q = me
Sec-Websocket-Origin: http: // localhost: 800
Sec-Websocket-Protocol: My-Custom-Chat-Protocol
الترقية: WebSocket
(استجابة التحدي): 52: DF: 2C: F4: 50: C2: 8E: 98: 14: B7: 7d: 09: CF: C8: 33: 40
طلب رأس
المضيف: مضيف خادم WebSocket
الاتصال: نوع الاتصال
الترقية: نوع ترقية البروتوكول
الأصل: زيارة المصدر
SEC-Websocket-Protocol: يتم تقسيم الاسم الاختياري ، Subprotocol ، المحدد بواسطة التطبيق نفسه ، وبروتوكولات متعددة بواسطة المسافات. (*خيار آخر المتبقي هو ملفات تعريف الارتباط)
SEC-Websocket-Key1: مفتاح مصادقة الأمان ، لا يمكن لطلب XHR صياغة الرؤوس التي تبدأ بـ "SEC-".
sec-websocket-key2: كما هو مذكور أعلاه
المفتاح 3: محتوى الجسم استجابة ، 8 بايت عشوائي.
رأس الاستجابة
Sec-Websocket-protocol: يجب أن يحتوي على اسم protocol المطلوب
Sec-Websocket-Origin: يجب أن تكون مساوية لمصدر الطلب
Sec-Websocket Location: يجب أن تكون مساوية للعنوان المطلوب
استجابة التحدي: محتوى هيئة الاستجابة ، المحسوب بناءً على المفاتيح الثلاثة في الطلب ، 16 بايت.
عملية حساب سلسلة الاستجابة Pseudocode:
part_1 = جميع الأرقام في key1 / عدد المسافات في key1 part_2 كما هو موضح أعلاه = big_endian (part_1)+big_endian (part_2)+key3challenge_response = md5_digest (sum) ؛
استراتيجية حساب Big_endian للأعداد الصحيحة 32 بت:
# يشبه إلى حد كبير حساب ألوان RGBA. من الوظيفة التالية ، يمكننا أن نرى أن عملية الحساب var big_endian = function (n) {return [3،2،1،0] .map (function (i) {return n >> 8*i & 0xff}) ؛} big_endian (0xcc77aaff) ؛ // -> 204 ، 119 ، 170 ، 255]2. إرسال البيانات
تم تصميم WebSocket API لمعالجة البيانات باستخدام الأحداث. يمكن للعملاء الحصول على بيانات كاملة طالما أنهم يحصلون على إشعارات الأحداث دون معالجة المخزن المؤقت يدويًا.
في هذه الحالة ، تسمى كل بيانات إطار. في تعريف المواصفات ، يجب أن يبدأ رأسه بـ 0x00 وينتهي سمة الذيل بـ 0xFF ، بحيث يكون لكل بيانات إرسال على الأقل بايتان.
في تطبيق الخادم ، يجب قطع الرأس والذيل عند تلقي البيانات ؛ بينما يجب تعبئة الرأس والذيل عند إرسال البيانات. التنسيق كما يلي:
# "Hello" تمثيل ثنائي أصلي ، رأس الطلب ، وهنا ترميزات UTF8
<Buffer E4 BD A0 E5 A5 BD>
# التمثيل الثنائي ملفوف.
<Buffer 00 E4 BD A0 E5 A5 BD FF>
ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون مفيدًا لتعلم الجميع.