1。契約
WebSocketは、TCP上のクライアントとサーバー間の完全なデュプレックス通信に基づくプロトコルです。 HTML5で定義されており、新世代のWebAppsの基本仕様の1つでもあります。
以前のAjaxの制限を突破し、キーはリアルタイムであり、サーバーはクライアントにコンテンツを積極的にプッシュできます!可能なアプリケーションには、マルチプレイヤーオンラインゲーム、ライブチャット、リアルタイム監視、リモートデスクトップ、ニュースサーバーなどが含まれます。
私にとっては、現時点で最も試してみたいのは、Canvas+Websocketの組み合わせでできることです。
2。実現
握手プロセスは標準のHTTP要求であるため、WebSocketを実装するには2つのオプションがあります。1)TCPでの実装。 2)既存のHTTPソフトウェアの実装。後者の利点は、既存のHTTPサーバーポートを共有できることであり、認証関数を再実装してHTTP要求を解析する必要がないことです。
この例では、ノードのHTTPモジュールが使用されています。 (TCPバージョンの添付ファイルとすべてのファイルを参照してください)
1。ノードサーバー側コード:
var http = require( 'http'); var url = require( 'url'); // var mime = require( 'mime'); var crypto = reques( '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( 'upgrade'、onupgrade); object.keys(req)、req.url、req ['upgrade']); if(!req.upgrade){//非アップグレード要求の選択:割り込みまたは通常のWebページの提供res.writehead(200、{'content-type': 'text/plain'}); res.write( 'websocket server Work!'); } res.End(); return;}; var onupgrade = function(req、sock、head){// console.log( 'method:'、object.keys(sock)); if(req.headers.upgrade!== 'websocket'){console.warn( 'Illegal Connection'); sock.end();戻る; } bind_sock_event(sock); {Handshake(Req、Sock、Head); } catch(e){console.error(e); sock.end(); }}}; //送信されるフレームをwrap var wrap = function(data){var fa = 0x00、fe = 0xff、data = data.toString()len = 2+buffer.bytelength(data)、buff = new buffer(len);バフ[0] = fa; buff.write(data、1);バフ[len-1] = fe; return buff;} //受信したフレームvar unwrap = function(data){return data.slice(1、data.length-1);} var bind_sock_event = function(sock){sock .on( 'data'、function(buffer){var data = unwrap(buffer); console.log( 'socket data:'+'+buffer'/'+buffer、'+buffer、unlap(buffer); send( 'hello html5、'+date.now())sock.emit( 'send'、data); sock.write(wrap(data)、 'binary'); var get_part = function(key){var empty = ''、key.replace(// s/g、empt)。 if(!spaces)throw {message: '誤ったキー:'+key、name: 'handshakeerror'} return get_big_endian(part / spaces);} var get_big_endian = function(n){return string.fromCharcode.Apply(null、[3,2,1,0] .map(function(i){unter(i)) function(key1、key2、head){var sum = get_part(key1) + get_part(key2) + head.toString( 'binary'); crypto.createhash( 'md5')。更新(sum).digest( 'binary');} var handshake(req、sock、head){var output = []、h = req.headers、br = '/r/n'; // header output.push( 'http/1.1 101 Websocketプロトコルハンドシェイク'、 'アップグレード:websocket'、 'connection:upgrade'、 'sec-websocket-origin:' + h.origin、 'sec-websocket-location:ws://' + h.host + req.url ); // body var c = callange(h ['sec-websocket-key1']、h ['sec-websocket-key2']、head); output.push(c); sock.write(output.join(br)、 'binary');}2。ブラウザクライアントコード:
<html> <head> <title> websocket demo </head> </head> <style type = "text/css"> textarea {width:400px; height:150px; display:block; overflow-y:scroll;} #output {width:600px; height:400; button {padding:.2em 1em;} </style> <link href = "layout.css" rel = "styleSheet" type = "text/css"/> <body> <body> <body> <body> <body> <body> <body> <body> <body> <body> <body> <body> <"output" readonly = "readonly"> </textarea> <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(e.type);} socket.onmessage = function(e){log( 'receive @'+new date()。tolocaletimestring()+'/n'+e.data); output.scrolltop = output.scrollheight} socket.onclose = function(e){log(e.type);} socket.addeventlistener( 'close'、function(){log( 'a another close handler ..');}、false); id( 'output')、input = id( 'input')、send = id( 'send'); var log = function(msg){output.textcontent += '>' +msg +'/n'} send.addeventlistener( 'click'、function(){socket.send.balue);3。詳細
HTTPプロトコルより上のWebSocketプロトコルの実装には、ハンドシェイクとデータの送信という2つのステップしかありません。
1。握手します
握手のプロセスは、チャレンジ応答と呼ばれます。最初に、クライアントはアップグレードという名前のHTTP Getリクエストを開始し、サーバーはリクエストを検証し、プロトコルのアップグレードが受け入れられ、ハンドシェイクが完了することを示す101の応答を与えます。
Chrome Inspectorによって美化された握手情報:
リクエストURL:ws://192.168.144.131:4400/pub/chat?q = me
リクエスト方法:get
ステータスコード: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:アプリケーション自体で定義されたオプション、サブプロトコル名、および複数のプロトコルはスペースで分割されます。 (*残っているもう1つのオプションはCookieです)
Sec-Websocket-Key1:セキュリティ認証キー、XHRリクエストは「sec-」から始まるリクエストヘッダーをforgeできません。
Sec-Websocket-Key2:上記と同じ
Key3:応答ボディコンテンツ、8バイトランダム。
応答ヘッダー
Sec-Websocket-Protocol:要求されたサブプロトコル名を含める必要があります
sec-websocket-origin:リクエストのソースに等しくなければなりません
SEC-Websocket-Location:要求されたアドレスに等しくなければなりません
チャレンジ応答:リクエストの3つのキー、16バイトに基づいて計算された応答ボディコンテンツ。
応答文字列計算プロセスpseudocode:
part_1 = key1 / key1 partの数 / key1 part_2上記と同じスペース数= big_endian(part_1)+big_endian(part_2)+key3challenge_response = md5_digest(sum);
32ビット整数のBig_Endian計算戦略:
#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で終了するため、各データ送信には少なくとも2バイトがあります。
サーバーの実装では、データを受信するときにヘッドとテールを切断する必要があります。一方、データを送信するときは頭と尾をパッケージ化する必要があります。フォーマットは次のとおりです。
# 'Hello'元のバイナリ表現、リクエストヘッダーとここにutf8エンコーディングがあります
<バッファーE4 BD A0 E5 A5 BD>
#ラップされたバイナリ表現。
<バッファー00 E4 BD A0 E5 A5 BD FF>
上記はこの記事に関するものです。すべての人の学習に役立つことを願っています。