介紹下websocket:
webSocket協議本質上是一個基於tcp的協議;
建立一個websocket連接,大體的過程:
1.客戶端瀏覽器首先向服務器發起一個http請求,這個請求和平常的請求有什么不同呢?
多了一點附加頭信息:"upgrade:web Socket” 表明我這申請的是一個websocket的http請求;
2.服務器收到請求后,解析這些附加的頭信息,然后產生應答信息返回給客戶端,這樣,連接就建立了;
3.雙方就可以通過這個連接通道自由的信息傳遞,這個連接會一直存在,直到一方自動關閉連接;
客戶端到服務端: GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Upgrade: WebSocket Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5 Origin: http://example.com [8-byte security key] 服務端到客戶端: HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade WebSocket-Origin: http://example.com WebSocket-Location: ws://example.com/demo [16-byte hash response]
從客戶端到服務端請求的信息里面包含:‘Sec-webSocket-key1","Sec-WebSocket-key2"和“[8-byte security key]”這樣的信息;這是客戶端瀏覽器需要向服務端提供的握手信息,服務端解析這些頭信息,並且在握手的過程中依據這些信息生成一個16位的安全密鑰並返回給客戶端,以表明服務器端獲取了客戶端的請求;
大致步驟:
1. 逐個字符讀取 Sec-WebSocket-Key1 頭信息中的值,將數值型字符連接到一起放到一個臨時字符串里,同時統計所有空格的數量; 2. 將在第 1 步里生成的數字字符串轉換成一個整型數字,然后除以第 1 步里統計出來的空格數量,將得到的浮點數轉換成整數型; 3. 將第 2 步里生成的整型值轉換為符合網絡傳輸的網絡字節數組; 4. 對 Sec-WebSocket-Key2 頭信息同樣進行第 1 到第 3 步的操作,得到另外一個網絡字節數組; 5. 將 [8-byte security key] 和在第 3,第 4 步里生成的網絡字節數組合並成一個 16 字節的數組; 6. 對第 5 步生成的字節數組使用 MD5 算法生成一個哈希值,這個哈希值就作為安全密鑰返回給客戶端,以表明服務器端獲取了客戶端的請求,同意創建 WebSocket 連接
var wsServer = 'ws://localhost:8888/Demo'; //連接地址 var websocket = new WebSocket(wsServer); //建立連接 websocket.onopen = function (evt) { onOpen(evt) }; //4個事件 websocket.onclose = function (evt) { onClose(evt) }; websocket.onmessage = function (evt) { onMessage(evt) }; websocket.onerror = function (evt) { onError(evt) }; function onOpen(evt) { console.log("Connected to WebSocket server."); } function onClose(evt) { console.log("Disconnected"); } function onMessage(evt) { console.log('Retrieved data from server: ' + evt.data); } function onError(evt) { console.log('Error occured: ' + evt.data); }
瀏覽器的支持情況:
瀏覽器 支持情況 Chrome Supported in version 4+ Firefox Supported in version 4+ Internet Explorer Supported in version 10+ Opera Supported in version 10+ Safari Supported in version 5+
正文來了:基於websocket制作的簡單聊天系統;
client.html:
<style> .kuang { width: 600px; min-height: 50px; max-height: 296px; border: 1px solid; float: left; display: block; position: relative; overflow-y: scroll; } </style> </head> <body> <div class="container"> <div class="row"> <div class="jumbotron bg-dark"> <h1 class="jumbotron-heading">WebSocket chat,歡迎使用:</h1> </div> <div class="input-group text-left"> <label>輸入用戶名:</label> <input type="text" id="name" /> <button id="conn">連接</button> <button id="close">斷開</button> </div> <div class="input-group text-muted"> <div class="kuang" id="mess"></div> </div> <hr class="featurette-divider"> <div class="input-group text-left"> <input type="text" class="value" id="value1" /> <button id="send">發送</button> </div> </div> </div>
簡單的界面,大致效果就是這樣的:
然后實現邏輯代碼:
var ws = new WebSocket('ws://127.0.0.1:8082'); ws.onopen = function (e) { console.log("連接服務器成功"); } ws.onmessage = function (e) { value1.removeAttribute("readOnly"); var time = new Date(); mess.innerHTML += time.toUTCString() + ":" + e.data + "<br>"; document.getElementById("send").onclick = function (e) { ws.send(input.value + "說:" + value1.value); value1.value = " "; } document.onkeydown = function (e) { e = e || window.event; if (e.keyCode == 13) { document.getElementById("send").onclick(); return false; } } } ws.onclose = function (e) { console.log("服務器關閉"); } ws.onerror = function () { console.log("連接出錯"); }
連接地址:ws://127.0.0.1:8082 那是哪里來的呢? (注意http請求則是寫成http://xxx,https請求則是https://xxx;ws當然是ws://xxx);
wbsocket只是客服端,地址當然是從我們的服務端給的呀;
服務端的搭建采用了一個這樣的庫:
var server = ws.createServer(function (conn) { conn.on('text', function (str) { }) conn.on("close", function (code, reason) { console.log("關閉連接"); }) conn.on("error", function (code, reason) { console.log("異常關閉"); }); }).listen(8082); console.log("websocket連接完畢")
好了,websocket連接算是建立啦!
下面展示下具體代碼:
client.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link rel="stylesheet" type="text/css" href="bootstrap-3.3.7-dist/css/bootstrap.min.css" /> <script src="jquery.min.js"></script> <script src="bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <style> .kuang { width: 600px; min-height: 50px; max-height: 296px; border: 1px solid; float: left; display: block; position: relative; overflow-y: scroll; } </style> </head> <body> <div class="container"> <div class="row"> <div class="jumbotron bg-dark"> <h1 class="jumbotron-heading">WebSocket chat,歡迎使用:</h1> </div> <div class="input-group text-left"> <label>輸入用戶名:</label> <input type="text" id="name" /> <button id="conn">連接</button> <button id="close">斷開</button> </div> <div class="input-group text-muted"> <div class="kuang" id="mess"></div> </div> <hr class="featurette-divider"> <div class="input-group text-left"> <input type="text" class="value" id="value1" /> <button id="send">發送</button> </div> </div> </div> <script> var input = document.getElementById("name"); var conn = document.getElementById("conn"); var close = document.getElementById("close"); var mess = document.getElementById("mess"); var value1 = document.getElementById("value1"); var pattern = /^[\u4e00-\u9fa5]{2,10}$/; close.disabled = true; if (window.WebSocket) { conn.onclick = function () { if (!pattern.test(input.value)) { alert("名稱不能為空且必須為中文"); return; } var ws = new WebSocket('ws://127.0.0.1:8082'); conn.disabled = true; close.disabled = false; ws.onopen = function (e) { console.log("連接服務器成功"); ws.send(input.value); input.setAttribute("readOnly", 'true'); value1.setAttribute("readOnly", 'true'); } ws.onmessage = function (e) { value1.removeAttribute("readOnly"); var time = new Date(); mess.innerHTML += time.toUTCString() + ":" + e.data + "<br>"; document.getElementById("send").onclick = function (e) { ws.send(input.value + "說:" + value1.value); value1.value = " "; } document.onkeydown = function (e) { e = e || window.event; if (e.keyCode == 13) { document.getElementById("send").onclick(); return false; } } } ws.onclose = function (e) { console.log("服務器關閉"); } ws.onerror = function () { console.log("連接出錯"); } close.onclick = function () { ws.onclose(); ws.send(input.value + 'close' + "了連接"); input.removeAttribute("readOnly"); conn.disabled = false; close.disabled = true; } } } </script> </body> </html>
server.js
var ws = require("nodejs-websocket"); console.log("開始建立連接..."); var str1 = null, str2 = null, clientReady = false, serverReady = false; var a = []; var server = ws.createServer(function (conn) { conn.on('text', function (str) { a.push(str); if (!clientReady) { if (a[0] === str) { str1 = conn; clientReady = true; str1.sendText("歡迎你" + str); } } else if (!serverReady) { if (str.indexOf('close') >= 0) { a.splice(2,1); clientReady = false; str1=null; return; } if (a[1] === str) { str2 = conn; serverReady = true; str2.sendText("歡迎你" + str); str1.sendText(str + "在線啦,你們可以聊天啦"); return; } } else if (clientReady && serverReady) { str2.sendText(str); str1.sendText(str); if (str.indexOf('close') >= 0) { a.splice(2, a.length); var len = a.length; for (var i = 0; i < len; i++) { // 定位該元素位置 if (str.indexOf(a[i])>=0) { a.splice(i,1); if(i==0){ str1=str2; } serverReady = false; str2=null; return; } } } } }) conn.on("close", function (code, reason) { console.log("關閉連接"); clientReady = false; serverReady = false; }) conn.on("error", function (code, reason) { console.log("異常關閉"); }); }).listen(8082); console.log("websocket連接完畢")
實現雙人聊天,client.html開啟兩個窗口就行!
詳細代碼在github上:
https://github.com/sulishibaobei/websocket-