websocket 学习--简单使用,nodejs搭建websocket服务器,到模拟股票,到实现聊天室
websocket簡介:
WebSocket協(xié)議是?HTML5 開始提供的一種基于TCP的一種新的全雙工通訊的網(wǎng)絡(luò)通訊協(xié)議。它允許服務(wù)器主動(dòng)發(fā)送信息給客戶端。
?
?
和http協(xié)議的不同??
HTTP 協(xié)議是一種無狀態(tài)的、無連接的、單向的應(yīng)用層協(xié)議。它采用了請求/響應(yīng)模型。通信請求只能由客戶端發(fā)起,服務(wù)端對請求做出應(yīng)答處理。這種通信模型有一個(gè)弊端:HTTP 協(xié)議無法實(shí)現(xiàn)服務(wù)器主動(dòng)向客戶端發(fā)起消息。而這種單向請求的特點(diǎn),注定了如果服務(wù)器有連續(xù)的狀態(tài)變化,客戶端要獲知就非常麻煩。
簡單的說,WebSocket協(xié)議之前,實(shí)現(xiàn)雙工通信就是通過不停發(fā)送HTTP請求(長輪詢,使用?Ajax 輪詢技術(shù),輪詢是在特定的的時(shí)間間隔(如每1秒),由瀏覽器對服務(wù)器發(fā)出HTTP請求),從服務(wù)器拉取更新來實(shí)現(xiàn),這導(dǎo)致了效率低下,浪費(fèi)帶寬資源,WebSocket解決了這個(gè)問題。
WebSocket 就是這樣發(fā)明的。WebSocket 連接允許客戶端和服務(wù)器之間進(jìn)行全雙工通信,以便任一方都可以通過建立的連接將數(shù)據(jù)推送到另一端。WebSocket 只需要建立一次連接,就可以一直保持連接狀態(tài)。這相比于輪詢方式的不停建立連接顯然效率要大大提高。
?
?
websocket如何工作??
?
在實(shí)現(xiàn)websocket連線過程中,需要通過瀏覽器發(fā)出websocket連線請求,然后服務(wù)器發(fā)出回應(yīng),這個(gè)過程通常稱為"握手" 。在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸相傳送。
?
?
瀏覽器通過 JavaScript 向服務(wù)器發(fā)出建立 WebSocket 連接的請求,連接建立以后,客戶端和服務(wù)器端就可以通過 TCP 連接直接交換數(shù)據(jù)。當(dāng)你獲取 Web Socket 連接后,你可以通過?send()?方法來向服務(wù)器發(fā)送數(shù)據(jù),并通過?onmessage?事件來接收服務(wù)器返回的數(shù)據(jù)。
?
以下 API 用于創(chuàng)建 WebSocket 對象。第一個(gè)參數(shù) url, 指定連接的 URL。而URL參數(shù)需要以WS://或者WSS://開頭,例如:ws://www.websocket.org,如果URL有語法錯(cuò)誤,構(gòu)造函數(shù)會(huì)拋出異常。第二個(gè)參數(shù) protocol 是可選的,指定了可接受的子協(xié)議。議的參數(shù)例如XMPP(Extensible?Messaging?and?Presence?Protocol)、SOAP(Simple?Object?Access?Protocol)或者自定義協(xié)議。??第二個(gè)參數(shù)是協(xié)議名稱,是可選的,服務(wù)端和客服端使用的協(xié)議必須一致,這樣收發(fā)消息彼此才能理解,你可以定義一個(gè)或多個(gè)客戶端使用的協(xié)議,服務(wù)端會(huì)選擇一個(gè)來使用,一個(gè)客服端和一個(gè)服務(wù)端之間只能有一個(gè)協(xié)議。
var Socket = new WebSocket(url, [protocol] );注意:基于多線程或多進(jìn)程的服務(wù)器無法適用于 WebSockets,因?yàn)樗荚诖蜷_連接,盡可能快地處理請求,然后關(guān)閉連接。任何實(shí)際的 WebSockets 服務(wù)器端實(shí)現(xiàn)都需要一個(gè)異步服務(wù)器。
目前大部分瀏覽器支持 WebSocket() 接口,如 Chrome, Mozilla, Opera 和 Safari。
?
?
WS和WSS的區(qū)別??
注意:WebSocket協(xié)議定義了兩種URL方案,WS和WSS分別代表了客戶端和服務(wù)端之間未加密和加密的通信。WS(WebSocket)類似于Http URL,而WSS(WebSocket Security)URL 表示連接是基于安全傳輸層(TLS/SSL)和https的連接是同樣的安全機(jī)制。
?
?
?
websocket的屬性、事件、方法
| Socket.readyState | 只讀屬性?readyState?表示連接狀態(tài),可以是以下值: 0 - 表示連接尚未建立。 1 - 表示連接已建立,可以進(jìn)行通信。 2 - 表示連接正在進(jìn)行關(guān)閉。 3 - 表示連接已經(jīng)關(guān)閉或者連接不能打開。 |
| Socket.bufferedAmount | 只讀屬性?bufferedAmount?已被 send() 放入正在隊(duì)列中等待傳輸,但是還沒有發(fā)出的 UTF-8 文本字節(jié)數(shù)。 |
注意:上述readyState?屬性用于表示鏈接狀態(tài)!
?
| open | Socket.onopen | 連接建立時(shí)觸發(fā) |
| message | Socket.onmessage | 客戶端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā) |
| error | Socket.onerror | 通信發(fā)生錯(cuò)誤時(shí)觸發(fā) |
| close | Socket.onclose | 連接關(guān)閉時(shí)觸發(fā) |
?
| Socket.send() | 使用連接發(fā)送數(shù)據(jù) |
| Socket.close() | 關(guān)閉連接 |
?
?
?
websocket+nodejs簡單實(shí)例應(yīng)用
WebSocket 協(xié)議本質(zhì)上是一個(gè)基于 TCP 的協(xié)議。為了建立一個(gè) WebSocket 連接,客戶端瀏覽器首先要向服務(wù)器發(fā)起一個(gè) HTTP 請求,這個(gè)請求和通常的 HTTP 請求不同,包含了一些附加頭信息,其中附加頭信息"Upgrade: WebSocket"表明這是一個(gè)申請協(xié)議升級的 HTTP 請求,服務(wù)器端解析這些附加的頭信息然后產(chǎn)生應(yīng)答信息返回給客戶端,客戶端和服務(wù)器端的 WebSocket 連接就建立起來了,雙方就可以通過這個(gè)連接通道自由的傳遞信息,并且這個(gè)連接會(huì)持續(xù)存在直到客戶端或者服務(wù)器端的某一方主動(dòng)的關(guān)閉連接。
WebSocket API是純事件驅(qū)動(dòng),一旦建立全雙工連接,當(dāng)服務(wù)端給客戶端發(fā)送數(shù)據(jù)或者資源,它能自動(dòng)發(fā)送狀態(tài)改變的數(shù)據(jù)和通知。所以你不需要為了狀態(tài)的更新而去輪訓(xùn)Server,在客戶端監(jiān)聽即可。
?
websocket客戶端:
<!DOCTYPE HTML> <html><head><meta charset="utf-8"><title>websocket測試(runoob.com)</title><script type="text/javascript">function WebSocketTest(){if ("WebSocket" in window){alert("您的瀏覽器支持 WebSocket!");// 初始化一個(gè) WebSocket 對象,參數(shù)指明urlvar ws = new WebSocket("ws://localhost:9999");// WebSocket 連接時(shí)候觸發(fā)ws.onopen = function(){//使用 send() 方法發(fā)送數(shù)據(jù)ws.send("客戶端發(fā)送的數(shù)據(jù)");alert("數(shù)據(jù)發(fā)送中...");};// 接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā)ws.onmessage = function (evt) { var received_msg = evt.data;console.log(received_msg);alert("數(shù)據(jù)已接收...");};//斷開 web socket 連接成功觸發(fā)事件ws.onclose = function(){ // 關(guān)閉 websocketalert("連接已關(guān)閉..."); };}else{// 瀏覽器不支持 WebSocketalert("您的瀏覽器不支持 WebSocket!");}}</script></head><body><div id="sse"><a href="javascript:WebSocketTest()">運(yùn)行 WebSocket</a></div></body> </html>?
websocket服務(wù)端:
WebSocket 在服務(wù)端的實(shí)現(xiàn)非常豐富。Node.js、Java、C++、Python 等多種語言都有自己的解決方案。這里主要記錄nodejs作為websocket服務(wù)端的解決方案。
Node 實(shí)現(xiàn)有以下三種。
- μWebSockets
- Socket.IO
- WebSocket-Node
?
這里主要記錄使用nodejs搭建websocket服務(wù)器的方案
ws 是nodejs的一個(gè)WebSocket庫,可以用來創(chuàng)建服務(wù)。使用cnpm install ws 命令行進(jìn)行安裝
下面是server.js的文件內(nèi)容,cmd轉(zhuǎn)到文件目錄運(yùn)行 node server.js? 命令行
?
var WebSocketServer = require('ws').Server, wss = new WebSocketServer({ port: 9999 }); wss.on('connection', function (ws) {console.log('client connected');ws.on('message', function (message) {console.log(message);ws.send("服務(wù)端接收到請求后,發(fā)送給客戶端的數(shù)據(jù)");});});效果如下:
?
?
?
進(jìn)階:websocket+nodejs模擬股票實(shí)例
上面的例子很簡單,只是為了演示如何運(yùn)用nodejs的ws創(chuàng)建一個(gè)WebSocket服務(wù)器。且可以接受客戶端的消息。那么下面這個(gè)例子演示股票的實(shí)時(shí)更新。客服端只需要連接一次,服務(wù)器端會(huì)不斷地發(fā)送新數(shù)據(jù),客戶端收數(shù)據(jù)后更新UI.頁面如下,有五只股票,開始和停止按鈕測試連接和關(guān)閉。
注意:一定要先在項(xiàng)目文件夾下運(yùn)行?cnpm install ws ?安裝wwebsocket依賴包,不然會(huì)報(bào)以下錯(cuò)誤
?
?
服務(wù)端server.js文件內(nèi)容如下:
//引入websocket 的ws模塊 var WebSocketServer = require('ws').Server,//初始化websocket對象 wss = new WebSocketServer({ port: 8181 });//初始數(shù)據(jù)對象 var stocks = {"AAPL": 95.0,"MSFT": 50.0,"AMZN": 300.0,"GOOG": 550.0,"YHOO": 35.0 }//獲取隨機(jī)數(shù)據(jù)的函數(shù) function randomInterval(min, max) {return Math.floor(Math.random() * (max - min + 1) + min); }//定時(shí)器返回的句柄 var stockUpdater; var randomStockUpdater=function(){for (var symbol in stocks) { //遍歷對象屬性進(jìn)行隨機(jī)增加浮動(dòng)數(shù)值if(stocks.hasOwnProperty(symbol)) { //遍歷對象非繼承屬性var randomizedChange = randomInterval(-150, 150);var floatChange = randomizedChange / 100;stocks[symbol] += floatChange;}}//隨機(jī)時(shí)間間隔,獲取一個(gè)數(shù)據(jù)區(qū)間中的隨機(jī)數(shù)值var randomMSTime = randomInterval(500, 2500); stockUpdater = setTimeout(function() { //模擬股票數(shù)據(jù)變化,隨機(jī)更改對象屬性值randomStockUpdater();}, randomMSTime);}//執(zhí)行模擬數(shù)據(jù)變化更新 randomStockUpdater();//聲明clientStocks接收客戶端數(shù)據(jù) var clientStocks = [];//連接建立后 wss.on('connection', function (ws) {//定義數(shù)據(jù)更新函數(shù)var sendStockUpdates = function (ws) {if (ws.readyState == 1) { //readyState為1表示已經(jīng)建立連接var stocksObj = {};for (var i = 0; i < clientStocks.length; i++) {var symbol = clientStocks[i];stocksObj[symbol] = stocks[symbol];}if (stocksObj.length !== 0) { //數(shù)據(jù)包內(nèi)容不為空時(shí)將數(shù)據(jù)響應(yīng)給客戶端ws.send(JSON.stringify(stocksObj));//需要將對象轉(zhuǎn)成字符串。WebSocket只支持文本和二進(jìn)制數(shù)據(jù)console.log("更新", JSON.stringify(stocksObj));}}}//服務(wù)器端定時(shí)更新響應(yīng)數(shù)據(jù)var clientStockUpdater = setInterval(function () {sendStockUpdates(ws);}, 1000);//服務(wù)器端接收到客戶端發(fā)送過來的數(shù)據(jù),根據(jù)請求的數(shù)據(jù)更新響應(yīng)數(shù)據(jù)ws.on('message', function (message) {var stockRequest = JSON.parse(message);console.log("服務(wù)器收到的消息:", stockRequest);clientStocks = stockRequest['stocks'];sendStockUpdates(ws);});});?
客戶端client.html 文件如下,界面使用了和jquery和bootstrape框架
<html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>WebSocket Demo</title><meta name="viewport" content="width=device-width, initial-scale=1"/><link href="../bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" /><script src="../js/jquery-1.12.3.min.js"></script><script src="../bootstrap-3.3.5/js/bootstrap.min.js"></script> </head><body> <div class="vertical-center"><div class="container"><h1>Stock Chart over WebSocket</h1><button class="btn btn-primary">開始</button><button class="btn btn-danger">停止</button><table class="table" id="stockTable"><thead><tr><th>Symbol</th><th>Price</th></tr></thead><tbody id="stockRows"><tr><td><h3>AAPL</h3></td><td id="AAPL"><h3><span class="label label-default">95.00</span></h3></td></tr><tr><td><h3>MSFT</h3></td><td id="MSFT"><h3><span class="label label-default">50.00</span></h3></td></tr><tr><td><h3>AMZN</h3></td><td id="AMZN"><h3><span class="label label-default">300.00</span></h3></td></tr><tr><td><h3>GOOG</h3></td><td id="GOOG"><h3><span class="label label-default">550.00</span></h3></td></tr><tr><td><h3>YHOO</h3></td><td id="YHOO"><h3><span class="label label-default">35.00</span></h3></td></tr></tbody></table></div> </div><script>//客戶端初始化websocket對象var ws = new WebSocket("ws://localhost:9999");//客戶端發(fā)送的請求對象var stock_request = { "stocks": ["AAPL", "MSFT", "AMZN", "GOOG", "YHOO"] };var isClose = false; //通訊連接是否被關(guān)閉//界面的初始化數(shù)據(jù)對象var stocks = {"AAPL": 0, "MSFT": 0, "AMZN": 0, "GOOG": 0, "YHOO": 0};//定義更新UI界面的函數(shù)function updataUI() {//websocket連接上時(shí)觸發(fā)ws.onopen = function (e) {console.log('Connection to server opened');isClose = false;ws.send(JSON.stringify(stock_request));console.log("sened a mesg");}// UI update functionvar changeStockEntry = function (symbol, originalValue, newValue) {var valElem = $('#' + symbol + ' span');valElem.html(newValue.toFixed(2)); //toFixed() 方法可把 Number 四舍五入為指定小數(shù)位數(shù)的數(shù)字。if (newValue < originalValue) {valElem.addClass('label-danger');valElem.removeClass('label-success');} else if (newValue > originalValue) {valElem.addClass('label-success');valElem.removeClass('label-danger');}}// websocket接收到服務(wù)端數(shù)據(jù)時(shí)觸發(fā)ws.onmessage = function (e) {var stocksData = JSON.parse(e.data); //字符串轉(zhuǎn)JSON對象console.log(stocksData);for (var symbol in stocksData) { //遍歷對象屬性,更改客戶端界面數(shù)據(jù)if (stocksData.hasOwnProperty(symbol)) {changeStockEntry(symbol, stocks[symbol], stocksData[symbol]);stocks[symbol] = stocksData[symbol];}}};}updataUI(); //更新UI界面$(".btn-primary").click(function() { //開始按鈕點(diǎn)擊可以在斷開后重連websocketif (isClose) {ws = new WebSocket("ws://localhost:9999");}updataUI(); //重連后更新UI界面});$(".btn-danger").click(function() { //斷開按鈕可以關(guān)閉websocket連接ws.close();});//觸發(fā)websocket連接關(guān)閉事件ws.onclose = function (e) {console.log("Connection closed", e);isClose = true;};</script> </body> </html>源碼見文章底部鏈接,效果如下:
?
?
?
進(jìn)階:websocket+nodejs模擬聊天室實(shí)例
?
上面的例子是連接建立之后,服務(wù)端不斷給客戶端發(fā)送數(shù)據(jù)。接下來例子是一個(gè)簡單的聊天室類的例子??梢越⒍鄠€(gè)連接。
1.安裝node-uuid模塊,用來給每個(gè)連接一個(gè)唯一號。
2、客戶端代碼如下:
<html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>WebSocket Echo Demo</title><meta name="viewport" content="width=device-width, initial-scale=1"/><link href="../bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" /><script src="../js/jquery-1.12.3.min.js"></script><script src="../js/jquery-1.12.3.min.js"></script><script src="../bootstrap-3.3.5/js/bootstrap.min.js"></script><script>//建立連接var ws = new WebSocket("ws://localhost:8181");var nickname = "";ws.onopen = function (e) {console.log('Connection to server opened');}//顯示函數(shù),根據(jù)客戶端接收到的數(shù)據(jù)類型進(jìn)行ui界面顯示function appendLog(type, nickname, message) {if (typeof message == "undefined") return;var messages = document.getElementById('messages');var messageElem = document.createElement("li");var preface_label;if (type === 'notification') {preface_label = "<span class=\"label label-info\">*</span>";} else if (type == 'nick_update') {preface_label = "<span class=\"label label-warning\">*</span>";} else {preface_label = "<span class=\"label label-success\">"+ nickname + "</span>";}var message_text = "<h2>" + preface_label + " "+ message + "</h2>";messageElem.innerHTML = message_text;messages.appendChild(messageElem);}//收到消息處理ws.onmessage = function (e) {var data = JSON.parse(e.data);nickname = data.nickname;appendLog(data.type, data.nickname, data.message);console.log("ID: [%s] = %s", data.id, data.message);}ws.onclose = function (e) {appendLog("Connection closed");console.log("Connection closed");}//發(fā)送消息function sendMessage() {var messageField = document.getElementById('message');if (ws.readyState === WebSocket.OPEN) {ws.send(messageField.value);}messageField.value = '';messageField.focus();}//修改名稱function changName() {var name = $("#name").val();if (ws.readyState === WebSocket.OPEN) {ws.send("/nick " + name);}}function disconnect() {ws.close();}</script> </head><body ><div class="vertical-center"><div class="container"><ul id="messages" class="list-unstyled"></ul><hr/><form role="form" id="chat_form" onsubmit="sendMessage(); return false;"><div class="form-group"><input class="form-control" type="text" id="message" name="message"placeholder="Type text to echo in here" value="" autofocus/></div><button type="button" id="send" class="btn btn-primary"onclick="sendMessage();">Send Message</button></form><div class="form-group"><span>nikename:</span><input id="name" type="text" /> <button class="btn btn-sm btn-info" onclick="changName();">change</button></div></div></div> </body> </html>?
3、服務(wù)端代碼如下:
//引入ws模塊,初始化websocket服務(wù)端 var WebSocket = require('ws'); var WebSocketServer = WebSocket.Server, wss = new WebSocketServer({ port: 8181 });//引入node-uuid模塊,唯一標(biāo)識 var uuid = require('node-uuid');//客戶端數(shù)組 var clients = [];//遍歷所有客戶端連接,依次下發(fā)數(shù)據(jù) function wsSend(type, client_uuid, nickname, message) {for (var i = 0; i < clients.length; i++) {var clientSocket = clients[i].ws;if (clientSocket.readyState === WebSocket.OPEN) {clientSocket.send(JSON.stringify({ //websocket傳遞JSONA字符串格式"type": type,"id": client_uuid,"nickname": nickname,"message": message}));}} }var clientIndex = 1;//每一個(gè)客戶端和服務(wù)端建立連接時(shí)觸發(fā) wss.on('connection', function(ws) {var client_uuid = uuid.v4(); //獲取隨機(jī)唯一標(biāo)識var nickname = "AnonymousUser" + clientIndex;clientIndex += 1;clients.push({ "id": client_uuid, "ws": ws, "nickname": nickname });console.log('client [%s] connected', client_uuid);var connect_message = nickname + " has connected";wsSend("notification", client_uuid, nickname, connect_message);console.log('client [%s] connected', client_uuid);ws.on('message', function(message) {if (message.indexOf('/nick') === 0) { //json字符串?dāng)?shù)據(jù)包含修改昵稱的數(shù)據(jù)時(shí)var nickname_array = message.split(' ');if (nickname_array.length >= 2) {var old_nickname = nickname;nickname = nickname_array[1];var nickname_message = "Client " + old_nickname + " changed to " + nickname;wsSend("nick_update", client_uuid, nickname, nickname_message);}} else {wsSend("message", client_uuid, nickname, message);}});//斷開指定uuid的連接var closeSocket = function(customMessage) {for (var i = 0; i < clients.length; i++) {if (clients[i].id == client_uuid) {var disconnect_message;if (customMessage) {disconnect_message = customMessage;} else {disconnect_message = nickname + " has disconnected";}wsSend("notification", client_uuid, nickname, disconnect_message);clients.splice(i, 1);}}};//某個(gè)客戶端連接斷開時(shí)觸發(fā)ws.on('close', function () {closeSocket();});//SIGINT這個(gè)信號是系統(tǒng)默認(rèn)信號,代表信號中斷,就是ctrl+cprocess.on('SIGINT', function () {console.log("Closing things");closeSocket('Server has disconnected');process.exit();}); });效果如下:
源碼見文章尾部
?
上述代碼實(shí)現(xiàn)了一個(gè)服務(wù)器下的多個(gè)客戶端連接,單并沒有實(shí)現(xiàn)客戶端的及時(shí)通訊,比如微信和QQ的單聊和群聊效果,可以參考以下demo
參考網(wǎng)址:https://blog.csdn.net/CJXBShowZhouyujuan/article/details/77816944
?
?
?
node-uuid是什么??
?
nodejs生成UID(唯一標(biāo)識符)——node-uuid模塊
unique identifier 惟一標(biāo)識符 ? ? ? ?-->> uid
在項(xiàng)目開發(fā)中我們常需要給某些數(shù)據(jù)定義一個(gè)唯一標(biāo)識符,便于尋找,關(guān)聯(lián)。node-uuid模塊很好的提供了這個(gè)功能。
?
使用起來很簡單,兩種:
1、uuid.v1(); -->基于時(shí)間戳生成 ?(time-based)
2、uuid.v4(); -->隨機(jī)生成 ?(random)
?
通常我們使用基于時(shí)間戳 ?v1() ?生成的UID,隨機(jī)生成 ?v4() ?還是有一定幾率重復(fù)的。
var UUID = require('uuid');var ID = UUID.v1();?
?
websocket 和 socket 的區(qū)別??
軟件通信有七層結(jié)構(gòu),下三層結(jié)構(gòu)偏向與數(shù)據(jù)通信,上三層更偏向于數(shù)據(jù)處理,中間的傳輸層則是連接上三層與下三層之間的橋梁,每一層都做不同的工作,上層協(xié)議依賴與下層協(xié)議?;谶@個(gè)通信結(jié)構(gòu)的概念。
Socket 其實(shí)并不是一個(gè)協(xié)議,是應(yīng)用層與 TCP/IP 協(xié)議族通信的中間軟件抽象層,它是一組接口。當(dāng)兩臺(tái)主機(jī)通信時(shí),讓 Socket 去組織數(shù)據(jù),以符合指定的協(xié)議。TCP 連接則更依靠于底層的 IP 協(xié)議,IP 協(xié)議的連接則依賴于鏈路層等更低層次。
WebSocket 則是一個(gè)典型的應(yīng)用層協(xié)議。
總的來說:Socket 是傳輸控制層協(xié)議,WebSocket 是應(yīng)用層協(xié)議。
?
?源碼:http://pan.baidu.com/s/1c2FfKbA
或 百度鏈接:https://pan.baidu.com/s/1cabjJKikHC3xBW-qUtP-3g? ?提取碼:yb4u?
?
參考網(wǎng)址:
https://www.cnblogs.com/stoneniqiu/p/5402311.html
http://www.runoob.com/html/html5-websocket.html
總結(jié)
以上是生活随笔為你收集整理的websocket 学习--简单使用,nodejs搭建websocket服务器,到模拟股票,到实现聊天室的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C指针原理(21)-C指针基础-ATT汇
- 下一篇: C指针原理(22)-C指针基础-att汇