精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


前言

服務器和客戶端保持長連接通信,實現方式比較多。有很多成熟的框架可以完成,底層無非都是對Socket流的封裝和使用。

一、SOCKET原理

Socket大致是指在端到端的一個連接中,這兩個端叫做Socket。

HTTP是基於傳輸層的TCP協議的,而Socket API也是,所以只是從使用上說,可以認為Socket和HTTP類似(HTTP是成文的互聯網協議,Socket是一直沿用的一種編程概念),是對於傳輸層協議的另一種直接使用。

1.1套接字(socket)概念

套接字(socket)是通信的基石,是支持TCP/IP協議的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示, 包含進行網絡通信必須的五種信息:

  • 連接使用的協議,
  • 本地主機的IP地址,
  • 本地進程的協議端口,
  • 遠地主機的IP地址,
  • 遠地進程的協議端口

應用層通過傳輸層進行數據通信時,TCP會遇到同時為多個應用程序進程提供併發服務的問題。多個TCP連接或多個應用程序進程可能需要通過同一個 TCP協議端口傳輸數據。 為了區別不同的應用程序進程和連接,許多計算機操作系統為應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應用層可以和傳輸層通過Socket接口,區分來自不同應用程序進程或網絡連接的通信,實現數據傳輸的併發服務。

1.2 建立socket連接

建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱為ClientSocket ,另一個運行於服務器端,稱為ServerSocket 。

套接字之間的連接過程分為三個步驟:服務器監聽,客戶端請求,連接確認。

服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。

客戶端請求

:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然後就向服務器端套接字提出連接請求。

連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶 端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。

注意,Socket並不是一種協議,更多的是為了方便開發者提供友好的編程方式。Socket通過門面模式實現了對TCP/IP的封裝,它實際對上層提供了一系列的接口,以方便我們在自己的應用中操作,而不必理會複雜的TCP/IP處理。這一網絡層次屬於數據傳輸層。


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


二、WebSocket

WebSocket protocol 是HTML5一種新的協議。目前除了IE瀏覽器,其他瀏覽器都基本支持。它實現了瀏覽器與服務器全雙工通信(full-duplex)。一開始的握手需要藉助HTTP請求完成。

他的目的是,即時通訊,替代輪詢。網站上的即時通訊是很常見的,比如網頁的QQ,聊天系統等。按照以往的技術能力通常是採用輪詢、Comet技術解決。

2.1 輪詢的缺點:

HTTP協議是非持久化的,單向的網絡協議,在建立連接後只允許瀏覽器向服務器發出請求後,服務器才能返回相應的數據。當需要即時通訊時,通過輪詢在特定的時間間隔(如1秒),由瀏覽器向服務器發送Request請求,然後將最新的數據返回給瀏覽器。

這樣的方法最明顯的缺點就是需要不斷的發送請求,而且通常HTTP request的Header是非常長的,為了傳輸一個很小的數據 需要付出巨大的代價,是很不合算的,佔用了很多的寬帶。

HTTP1.1默認使用長連接,使用長連接的HTTP協議,會在響應頭中加入下面這行信息: Connection:keep-alive

然而WebSocket的出現可以彌補這一缺點。在WebSocket中,只需要服務器和瀏覽器通過HTTP協議進行一個握手的動作,然後單獨建立一條TCP的通信通道進行數據的傳送。

2.2 WebSocket原理

WebSocket同HTTP一樣也是應用層的協議,但是它是一種雙向通信協議,是建立在TCP之上的。

WebSocket是一種在單個TCP連接上進行全雙工通信的協議。WebSocket API也被W3C定為標準。

WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手, 兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。

握手過程:

  1. 瀏覽器、服務器建立TCP連接,三次握手。這是通信的基礎,傳輸控制層,若失敗後續都不執行。
  2. TCP連接成功後,瀏覽器通過HTTP協議向服務器傳送WebSocket支持的版本號等信息。(開始前的HTTP握手)
  3. 服務器收到客戶端的握手請求後,同樣採用HTTP協議回饋數據。
  4. 當收到了連接成功的消息後,通過TCP通道進行傳輸通信。

Websocket默認使用請求協議為:ws://,默認端口:80。對TLS加密請求協議為:wss://,端口:443。

2.3 webSocket與HTTP的關係

相同點

  1. 都是一樣基於TCP的,都是可靠性傳輸協議。
  2. 都是應用層協議。

不同點:

  1. WebSocket是雙向通信協議,模擬Socket協議,可以雙向發送或接受信息。
  2. HTTP是單向的。
  3. WebSocket是需要握手進行建立連接的。

聯繫: WebSocket在建立握手時,數據是通過HTTP傳輸的。但是建立之後,在真正傳輸時候是不需要HTTP協議的。

2.4 WebSocket與Socket的關係

Socket其實並不是一個協議,而是為了方便使用TCP或UDP而抽象出來的一層,是位於應用層和傳輸控制層之間的一組接口。tcp是可靠的連接,且連接後才可以發送數據;udp是不可靠的連接,不連接就可以發送數。

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

當兩臺主機通信時,必須通過Socket連接,Socket則利用TCP/IP協議建立TCP連接。TCP連接則更依靠於底層的IP協議,IP協議的連接則依賴於鏈路層等更低層次。

WebSocket則是一個典型的應用層協議,Socket是傳輸控制層協議。


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


2.5 WebSocket 特點以及常見概念

  • 支持瀏覽器/Nodejs環境
  • 支持雙向通信
  • API簡單易用
  • 支持二進制傳輸
  • 減少傳輸數據量

2.5.1 長輪詢

http 長輪詢是服務器收到請求後如果有數據, 立刻響應請求; 如果沒有數據就會 hold 一段時間,這段時間內如果有數據立刻響應請求; 如果時間到了還沒有數據, 則響應 http 請求;瀏覽器受到 http 響應後立在發送一個同樣http 請求查詢是否有數據;


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


2.5.2 短輪詢:

客戶端不管是否收到服務端的response數據,都會定時向服務端發送請求,查詢是否有數據更新。 http 短輪詢的侷限是實時性低;


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


長輪詢與短輪詢兩者相同點: 可以看出 http 長輪詢和 http 短輪詢的都會 hold 一段時間;

長輪詢與短輪詢兩者不同點 間隔發生在服務端還是瀏覽器端: http 長輪詢在服務端會 hold 一段時間, http 短輪詢在瀏覽器端 “hold”一段時間; 應用: http長輪詢一般用在 web im, im 實時性要求高, http 長輪詢的控制權一直在服務器端, 而數據是在服務器端的,因此實時性高;

http 短輪詢一般用在實時性要求不高的地方, 比如新浪微薄的未讀條數查詢就是瀏覽器端每隔一段時間查詢的. setInterval

2.5.3 長連接

  1. 長連接是指的TCP連接,而不是HTTP連接
  2. 長連接意味著連接會被複用
  3. 服務器和客戶端都設置 Connection: keep-alive
  4. 現在基本用的HTTP1.1協議,HTTP1.1默認長連接

所謂 http 長連接, 就是多個 http 請求共用一個 tcp 連接; 這樣可以減少多次臨近 http 請求導致 tcp建立關閉所產生的時間消耗. http 1.1 中在請求頭和相應頭中用 connection字段標識是否是 http長連接, connection: keep-alive, 表明是 http 長連接; connection:closed, 表明服務器關閉 tcp 連接.

與 connection 對應的一個字段是 keep-live, http 響應頭中出現, 他的格式是 timeout=30,max=5, timeout 是兩次 http 請求保持的時間(s), , max 是這個 tcp 連接最多為幾個 http請求重用

指在一個TCP連接上可以發送多個數據包,在TCP連接保持期間,如果沒有數據包發送,則雙方就需要發送心跳包來維持此連接。 連接過程: 建立連接——數據傳輸——…——(發送心跳包,維持連接)——…——數據傳輸——關閉連接

好處:

比如請求一個普通的網頁,這個網頁裡肯定包含了若干CSS、JS等一系列資源,如果是短連接(也就是每次都要重新建立TCP連接)的話,那每次打開一個網頁,基本就要建立幾個甚至幾十個TCP連接,浪費很多網絡資源。如果是長連接的話,那麼這麼多HTTP請求(包括請求網頁的內容、CSS文件、JS文件、圖片等)都是使用的一個TCP連接,顯然可以節省很多資源。

另外一點,長連接並不是永久連接的。如果一段時間內(具體時間可以在header中進行設置,也就是所謂的超時時間),這個連接沒有HTTP請求發出的話,那麼這個長連接就會被斷掉。

注意:因為HTTP1.1協議以後,連接默認都是長連接。沒有短連接說法(HTTP1.0默認使用短連接),網上大多數指的長短連接實質上說的就是TCP連接。

2.5.4 短連接

指通信雙方有數據交互時,建立一個TCP連接,數據發送完成之後,則斷開此連接。 連接過程: 建立連接——數據傳輸——斷開連接——…——建立連接——數據傳輸——斷開連接

目前 http 協議普遍使用的是 1.1 版本, 之前有個 1.0 版本,兩者之間的一個區別是 1.1 支持 http 長連接

http1.0 不支持 http 長連接, 每次一個 http請求響應後都關閉 tcp 連接, 下個 http 請求會重新建立 tcp 連接.

2.5.5 通訊概念

  • 單工 數據傳輸的方向唯一,只能由發送方向接收方的單一固定方向傳輸數據。
  • 半雙工 即通信雙方既是接收方也是發送方,不過,在某一時刻只能允許向一個方向傳輸數據。
  • 全雙工: 即通信雙方既是接收方也是發送方,兩端設備可以同時發送和接收數據。

單工、半雙工和全雙工 這三者都是建立在 TCP協議(傳輸層上)的概念,不要與應用層進行混淆。

三、HTTP與TCP的區別和聯繫

3.1、TCP連接

建立起一個TCP連接需要經過“三次握手”:

第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;

第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;

第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。

握手過程中傳送的包裡不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連 接之前,TCP 連接都將被一直保持下去。斷開連接時服務器和客戶端均可以主動發起斷開TCP連接的請求,斷開過程需要經過“四次握手”(過程就不細寫 了,就是服務器和客戶端交互,最終確定斷開)

3.2、HTTP連接

HTTP協議即超文本傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。

HTTP連接最顯著的特點是客戶端發送的每次請求都需要服務器回送響應,在請求結束後,會主動釋放連接。從建立連接到關閉連接的過程稱為“一次連接”。

1)在HTTP 1.0中,客戶端的每次請求都要求建立一次單獨的連接,在處理完本次請求後,就自動釋放連接。

2)在HTTP 1.1中則可以在一次連接中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再發送下一個請求。

由於HTTP在每次請求結束後都會主動釋放連接,因此HTTP連接是一種“短連接”,要保持客戶端程序的在線狀態,需要不斷地向服務器發起連接請求。通常的 做法是即時不需要獲得任何數據,客戶端也保持每隔一段固定的時間向服務器發送一次“保持連接”的請求,服務器在收到該請求後對客戶端進行回覆,表明知道客 戶端“在線”。若服務器長時間無法收到客戶端的請求,則認為客戶端“下線”,若客戶端長時間無法收到服務器的回覆,則認為網絡已經斷開。

  • TCP是底層通訊協議,定義的是數據傳輸和連接方式的規範
  • HTTP是應用層協議,定義的是傳輸數據的內容的規範
  • HTTP協議中的數據是利用TCP協議傳輸的,所以支持HTTP也就一定支持TCP
  • HTTP支持的是www服務 ;
  • 而TCP/IP是協議是Internet國際互聯網絡的基礎。TCP/IP是網絡中使用的基本的通信協議。

四、nodejs創建web服務器和Tcp服務器

4.1 使用Express創建Web服務器

<code>var express = require('express');
var app = express();

app.use(express.static('./public'))
app.get('/',(req,res,next)=>{
  res.end('hello');
  next(); // 進行下一步,打印日誌 
})

//Router方法 適合某一個模塊下的多個子路由
var Router = express.Router();
Router.get('/add',(req,res)=>{
  res.end('add')
})
Router.get('/list',(req,res)=>{
  res.end('list')
})
app.use('/post', Router)

//route方法  適合restful API
app.route('/article')
  .get((req,res)=>{
    res.end('/article get')
  })
  .post((req,res)=>{
    res.end('/article post')
  })
app.get('/news/:newsId', (req, res)=>{
  req.end('newsId:' + req.newsId);
})    

app.listen(18001, function afterLister(){
  console.log('服務再次啟動')
})

複製代碼/<code>

4.2 創建TCP服務器

使用Node.js創建TCP服務器,首先要使用require(‘net’)來加載net模塊,之後使用net模塊的createServer方法就可以創建一個TCP服務器.

  • 使用net模塊創建TCP服務器
  • 使用telnet連接TCP服務器
  • 使用net創建TCP客戶端

下面代碼構建一個TCP服務器:

<code>//引入net模塊
var net=require('net');
//創建TCP服務器
var server=net.createServer(function(socket){
    console.log('someone connets');
})
server.listen(18001,function(){
    console.log('server is listening');
});
複製代碼/<code>

當在瀏覽器中輸入http://localhost:18001/,連接成功後就會打印出someone connets字樣,表明createServer方法的回調函數已經執行,說明已經成功連接這個TCP服務器。

服務端:tcp.js:

<code>const HOST = '127.0.0.1';
//引入net模塊
var net=require('net');

//創建TCP服務器
var server=net.createServer(function(socket){
    console.log('someone connets');
    //     //獲取地址信息
    var address=server.address();
    //獲取地址端口
    console.log('the port  is '+address.port);
    console.log('the address  is '+address.address);
    var message='client,the server address is'+JSON.stringify(address);
     //發送數據
     socket.write(message,function(){
        var writeSize=socket.bytesWritten; 
        console.log(message+'has send');
        console.log('the size of message is'+writeSize);
    });

    //監聽dada事件
    socket.on('data',function(data){
        //打印data
        console.log(data.toString());
        socket.write('server write:test ')
    });
});
 //設置監聽端口
server.listen(18001,HOST,function(){  
    console.log('server is listening');
});

複製代碼/<code>

啟動服務 node tcp.js

客戶端 cmd

1> 在cmd 控制檯輸入模擬客戶端: telnet 127.0.0.1 18001

但是需要先通過控制面板把 telnet 勾選上,如下圖所示:


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


2> 或者通過網絡調試助手模擬客戶端和服務端 調試


精讀Websocket原理大全需知,以及具體使用(ws+socket.io)


網絡調試助手調試下載地址:

<code>鏈接:https://pan.baidu.com/s/1bZQm1f9UtBKPSAIyqM4Nyw 
提取碼:d9ck
複製代碼/<code> 

參考:www.onlinedown.net/soft/971066…

客戶端 tcpClient.js:

<code>var net = require('net')

const PORT = 18001;
const HOST = '127.0.0.1';

var tcpClient = net.Socket();
tcpClient.connect(PORT, HOST, function(){
  console.log('客戶端發送信息成功打印')
  tcpClient.write('客戶端發送信息成功')
});

tcpClient.on('data',(data)=>{
  console.log( data.toString())
})

複製代碼/<code>

五、websocket 常用框架

  • socket.io
  • Ws

5.1 WebSocket事件:

WebSocket API是純事件驅動,通過監聽事件可以處理到來的數據和改變的鏈接狀態。客戶端不需要為了更新數據而輪訓服務器。服務端發送數據後,消息和事件會異步到達。WebSocket編程遵循一個異步編程模型,只需要對WebSocket對象增加回調函數就可以監聽事件。你也可以使用addEventListener()方法來監聽。而一個WebSocket對象分四類不同事件。

5.1.1 open:

一旦服務端響應WebSocket連接請求,就會觸發open事件。響應的回調函數稱為onopen。

<code>ws.onopen = function(e) {
    console.log("Connection open...");
};
複製代碼/<code>

open事件觸發的時候,意味著協議握手結束,WebSocket已經準備好收發數據。如果你的應用收到open事件,就可以確定服務端已經處理了建立連接的請求,且同意和你的應用通信。

5.1.2 Message

當消息被接受會觸發消息事件,響應的回調函數叫做onmessage。如下: // 接受文本消息的事件處理實例:

<code>ws.onmessage = function(e) {
    if(typeof e.data === "string"){
    console.log("String message received", e, e.data);
    } else {
    console.log("Other message received", e, e.data);
}
};
複製代碼/<code>

除了文本消息,WebSocket消息機制還能處理二進制數據,有Blob和ArrayBuffer兩種類型,在讀取到數據之前需要決定好數據的類型。

<code>// 設置二進制數據類型為blob(默認類型)
ws.binaryType = "blob";
// Event handler for receiving Blob messages
ws.onmessage = function(e) {
    if(e.data instanceof Blob){
    console.log("Blob message received", e.data);
    var blob = new Blob(e.data);
}
};
//ArrayBuffer
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
if(e.data instanceof ArrayBuffer){
    console.log("ArrayBuffer Message Received", + e.data);// e.data即ArrayBuffer類型
    var a = new Uint8Array(e.data);
}
};

複製代碼/<code>

5.1.3 Error

如果發生意外的失敗會觸發error事件,相應的函數稱為onerror,錯誤會導致連接關閉。如果你收到一個錯誤事件,那麼你很快會收到一個關閉事件,在關閉事件中也許會告訴你錯誤的原因。而對錯誤事件的處理比較適合做重連的邏輯。

<code>//異常處理
ws.onerror = function(e) {
   console.log("WebSocket Error: " , e);
};

複製代碼/<code>

5.1.4 Close

不言而喻,當連接關閉的時候回觸發這個事件,對應onclose方法,連接關閉之後,服務端和客戶端就不能再收發消息。 當然你可以調用close方法斷開與服務端的鏈接來觸發onclose事件,

<code>ws.onclose = function(e) {
    console.log("Connection closed", e);
};
複製代碼/<code>

5.2 WebSocket事件和方法:

5.2.1 send

一旦在服務端和客戶端建立了全雙工的雙向連接,可以使用send方法去發送消息, //發送一個文本消息 ws.send("Hello WebSocket!");

當連接是open的時候send()方法傳送數據,當連接關閉或獲取不到的時候回拋出異常。

注意:必須是open之後才可以send數據

<code>var ws = new WebSocket("ws://echo.websocket.org")
ws.onopen = function(e) {
    ws.send("Initial data");
}
複製代碼/<code>

如果想通過響應別的事件去發送消息,可以檢查readyState屬性的值為open的時候來實現。

<code>function myEventHandler(data) {
if (ws.readyState === WebSocket.OPEN) {
    //open的時候即可發送
    ws.send(data);
} else {
    // Do something else in this case.
}
}
複製代碼/<code>

發送二進制數據:

<code>// Send a Blob
var blob = new Blob("blob contents");
ws.send(blob);
// Send an ArrayBuffer
var a = new Uint8Array([8,6,7,5,3,0,9]);
ws.send(a.buffer);
複製代碼/<code>

Blob對象和JavaScript File API一起使用的時候相當有用,可以發送或接受文件,大部分的多媒體文件,圖像,視頻和音頻文件。這一章末尾會結合File API提供讀取文件內容來發送WebSocket消息的實例代碼。

5.2.2 close()

使用close方法來關閉連接,如果連接以及關閉,這方法將什麼也不做。調用close方法只後,將不能發送數據。 ws.close();

六、 websocket服務端基於ws的搭建

1.簡單安裝

安裝ws模塊,npm install ws ws:是nodejs的一個WebSocket庫,可以用來創建服務。 github.com/websockets/…

WebSocket協議定義了兩種URL方案,WS和WSS分別代表了客戶端和服務端之間未加密和加密的通信。WS(WebSocket)類似於Http URL,而WSS(WebSocket Security)URL 表示連接是基於安全傳輸層(TLS/SSL)和https的連接是同樣的安全機制。

2.服務端server.js配置

在項目裡面新建一個server.js,創建服務,指定8181端口,將收到的消息log出來。

<code>var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
    console.log('client connected');
    ws.on('message', function (message) {
        console.log(message);
    });
});

複製代碼/<code>

服務端完整代碼如下:

<code>var WebSocket = require('ws').Server;

let wss = new WebSocket({
    port:8181
})
// wss 繼承eventEmitter

wss.on('connection',function(ws){
    //鏈接成功
    console.log('建立鏈接')
    // 監聽客戶端發送的信息
    ws.on('message',function(data){
        console.log(data);

        //單一:誰給我發, 我就給誰發
        ws.send('hello' + data )

        //群發 

    })

    ws.on('close',function(){
        console.log('關閉鏈接')
    })
});

複製代碼/<code>

3.客戶端client.html建立WebSocket鏈接

我們需要通過調用WebSocket構造函數來創建一個WebSocket連接,構造函數會返回一個WebSocket實例,可以用來監聽事件。這些事件會告訴你什麼時候連接建立,什麼時候消息到達,什麼時候連接關閉了,以及什麼時候發生了錯誤。

WebSocket的構造函數需要一個URL參數和一個可選的協議參數(一個或者多個協議的名字),協議的參數例如XMPP、SOAP(Simple Object Access Protocol)或者自定義協議。而URL參數需要以WS://或者WSS://開頭,例如:ws://www.websocket.org

在頁面上建立一個WebSocket的連接。用send方法發送消息。

<code>var ws = new WebSocket("ws://localhost:8181");
    ws.onopen = function (e) {
        console.log('Connection to server opened');
    }
    function sendMessage() {
        ws.send($('#message').val());
    }

複製代碼/<code>

具體完整代碼如下:

<code>  
            
   
複製代碼/<code>

運行之後如下,服務端即時獲得客戶端的消息。

七、websocket服務端基於socket.io的搭建

socket.io就是眾多websocket庫中的一種,它並不像其它庫那樣簡單地實現了一下websocket,而是在websocket外面包裹了厚厚的一層。普通的websocket(例如ws庫)只需要服務端就夠了,socket.io自定義了一種基於websocket的協議,所以socket.io的服務端和客戶端必須配套。簡言之,如果服務端使用socket.io,那麼客戶端就沒得選了,必然也用socket.io的客戶端。

  • socket.io 是 基於engine.io 進行封裝的庫
  • socket.io 是基於 Websocket 的Client-Server 實時通信庫。
  • socket.io 底層使用engine.io 封裝了一層協議。

socket.io提供了一個房間(Namespace)概念。當客戶端創建一個新的長連接時,就會分配一個新的Namespace進行區分。

7.1 socket.io事件

7.1.1 socket.io服務器的系統事件

io.on('connection', callback): 有新Socket連接建立時 socket.on('message', callback): 當有socket..send()方法發送完信息後觸發 socket.on('disconnect', callback): 當連接斷開時觸發

7.1.2 客戶端的系統事件

socket.io.on(‘open’, callback): 當socket客戶端開啟與服務器的新連接時觸發 socket.io.on(‘connect’, callback):當socket客戶端連接到服務器後觸發 socket.io.on(‘connect_timeout’, callback):當socket客戶端與服務器連接超時後觸發 socket.io.on(‘connec_errort’, callback):當socket客戶端連接服務器失敗時觸發 socket.io.on(‘connec_attemptt’, callback):當socket客戶端嘗試重新連接到服務器後觸發 socket.io.on(‘reconnect’, callback):當socket客戶端重新連接到服務器後觸發 socket.io.on(‘reconnect_error’, callback):當socket客戶端重新連接服務器失敗後觸發 socket.io.on(‘reconnect_fail’, callback):當socket客戶端重新連接到服務器失敗後觸發 socket.io.on(‘close’, callback):當socket客戶端關閉與服務器的連接後觸發

7.2 原生 nodejs 結合 Socket.io 實現服務器和 客戶端的相互通信

1、 安裝 Socket.io 2、 寫原生的 JS,搭建一個服務器,server 創建好之後,創建一個 io 對象。 3、 服務器端通過 emit 廣播,通過 on 接收廣播 4、 客戶端端通過 emit 廣播,通過 on 接收廣播

WebSocket protocol是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通信,同時允許跨域通訊,是server push技術的一種很好的實現。我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。

7.2.1 為什麼需要 WebSocket

瞭解計算機網絡協議的人,應該都知道:HTTP 協議是一種無狀態的、無連接的、單向的應用層協議。它採用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。

這種通信模型有一個弊端:HTTP 協議無法實現服務器主動向客戶端發起消息。

7.2.2 具體步驟和使用

1、 安裝 Socket.io

網址:socket.io/

<code>npm install socket.io 
或者 yarn add  socket.io
複製代碼/<code>

2、搭建一個server 服務器, 創建一個 io 對象。

<code>var http = require('http');
var path = require('path');
var fs = require('fs');

//引入socket.io
var socket = require('socket.io');

var app = http.createServer(function(req,res){
     //加載靜態頁面

    fs.readFile(path.resolve(__dirname,'app.html'),function(err,data){
        if(req.url=="/"){ 
            res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});
            res.end(data);
        }
    })

})

// socket 與 http服務建立連接
var io = socket(app);

// 監聽連接事件 
io.on('connection',function(sock){
    console.log(sock)
    console.log('服務器建立連接了');
})

app.listen(3000,'127.0.0.1');
複製代碼/<code>

socket 與 http服務建立連接後,你就會發現,http://127.0.0.1:3000/socket.io/socket.io.js 就是一個 js 文件 的地址了。

現在需要製作一個客戶端的 index 頁面,這個頁面中,必須引用秘密 js 文件。調用 io 函數,取得 socket 對象。

index.html

<code>


    
    
    客戶端
     


    
     



複製代碼/<code>

這個時候會在終端打印出 '一個客戶端與服務器建立連接'

3、服務器端通過 emit 廣播,通過 on 接收廣播

<code>
//引入socket.io
var socket = require('socket.io');

// socket 與 http服務建立連接
var io = socket(app);

// 監聽連接事件 
io.on('connection',function(sock){
    console.log('一個客戶端與服務器建立連接了');

    //服務器獲取客戶端廣播的數據
    sock.on('addcart', function(data){
        console.log(data);

          //服務器給客戶端發送數據
            //socket.emit();   /*誰給我發信息我把信息廣播給誰*/
            //io.emit() ;   /*群發  給所有連接服務器的客戶都廣播數據*/

        socket.emit('to-client','我是服務器的數據'+data.client);

        //io.emit('to-client','我是服務器的數據'+data.client);

    })

    sock.on('disconnect',function(){
        console.log('斷開連接了');
    })

})
複製代碼/<code>

每一個連接上來的用戶,都有一個 socket。 由於我們的 emit 語句,是 socket.emit()發 出的,所以指的是向這個客戶端發出語句。

廣播,就是給所有當前連接的用戶發送信息:

<code>io.emit('to-client','我是服務器的數據'+data.client);
 /*群發  給所有連接服務器的客戶都廣播數據*/

複製代碼/<code>

4. 客戶端端通過 emit 廣播,通過 on 接收廣播

<code> 
     
     

複製代碼/<code> 

7.3 Express 結合 Socket.io 實現服務器和客戶 端的相互通信

第一個目標是建立一個簡單的HTML網頁,提供表單和消息列表。

<code>var app = require('express')();
//創建http server ,  createServer 與 Server作用一樣都是創建server
var http = require('http').createServer(app);
//var http = require('http').Server(app);

app.get('/', (req, res) => {
  res.send('

Hello world

'); }); http.listen(3000, () => { console.log('listening on *:3000'); }); 複製代碼/<code>

Express將app初始化為可以提供給HTTP服務器的函數處理程序 我們定義了一個路由處理程序,當我們訪問我們的網站主頁時,它會被調用。

我們讓http服務器監聽端口3000。

7.3.1 引入 Socket.IO

npm install socket.io

注意:socket.io基於http服務(socket.io的參數就是http server)

<code>var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
  console.log('a user connected');
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});
複製代碼/<code>

7.3.2 寫socket的代碼

<code>//3、寫socket的代碼
io.on('connection', function (socket) {
      console.log('a user connected');
      socket.on('message',function(key){
          console.log(key);
  
         //io.emit  廣播
        //socket.emit  誰給我發的信息我回返回給誰
          //io.emit('servermessage',data);   /*服務器給客戶端發送數據*/

         //   機器人聊天
        //   console.log(robot[key])
        //   socket.emit('servermessage',robot[key]);

          io.emit('servermessage',key.msg);

      })
  });
複製代碼/<code>

7.3.3 群聊

io.emit 廣播 只要接收到客戶端的信息, 就把這個信息發送給連接到這個服務的所有客戶端用戶

io.emit('servermessage',data); /服務器給客戶端發送數據/

7.3.4 機器人聊天

socket.emit 誰給我發的信息我回返回給誰 具體代碼如上:

<code>socket.emit('servermessage',robot[key]);
複製代碼/<code>

7.3.5 客戶端 代碼

<code>  
/<code>


最後,咱給小編:

1. 點贊+評論

2. 點頭像關注,轉發給有需要的朋友。

謝謝!!


分享到:


相關文章: