日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

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

 

前言

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

一、SOCKET原理

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

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

1.1套接字(socket)概念

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

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

應(yīng)用層通過傳輸層進行數(shù)據(jù)通信時,TCP會遇到同時為多個應(yīng)用程序進程提供并發(fā)服務(wù)的問題。多個TCP連接或多個應(yīng)用程序進程可能需要通過同一個 TCP協(xié)議端口傳輸數(shù)據(jù)。 為了區(qū)別不同的應(yīng)用程序進程和連接,許多計算機操作系統(tǒng)為應(yīng)用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口。應(yīng)用層可以和傳輸層通過Socket接口,區(qū)分來自不同應(yīng)用程序進程或網(wǎng)絡(luò)連接的通信,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。

1.2 建立socket連接

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

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

服務(wù)器監(jiān)聽:服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實時監(jiān)控網(wǎng)絡(luò)狀態(tài),等待客戶端的連接請求。

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

連接確認:當服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求時,就響應(yīng)客戶端套接字的請求,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)給客戶 端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接收其他客戶端套接字的連接請求。

注意,Socket并不是一種協(xié)議,更多的是為了方便開發(fā)者提供友好的編程方式。Socket通過門面模式實現(xiàn)了對TCP/IP的封裝,它實際對上層提供了一系列的接口,以方便我們在自己的應(yīng)用中操作,而不必理會復(fù)雜的TCP/IP處理。這一網(wǎng)絡(luò)層次屬于數(shù)據(jù)傳輸層。

 

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

 

二、WebSocket

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

他的目的是,即時通訊,替代輪詢。網(wǎng)站上的即時通訊是很常見的,比如網(wǎng)頁的QQ,聊天系統(tǒng)等。按照以往的技術(shù)能力通常是采用輪詢、Comet技術(shù)解決。

2.1 輪詢的缺點:

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

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

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

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

2.2 WebSocket原理

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

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

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

握手過程:

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

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

2.3 webSocket與HTTP的關(guān)系

相同點

  1. 都是一樣基于TCP的,都是可靠性傳輸協(xié)議。
  2. 都是應(yīng)用層協(xié)議。

不同點:

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

聯(lián)系: WebSocket在建立握手時,數(shù)據(jù)是通過HTTP傳輸?shù)摹5墙⒅螅谡嬲齻鬏敃r候是不需要HTTP協(xié)議的。

2.4 WebSocket與Socket的關(guān)系

Socket其實并不是一個協(xié)議,而是為了方便使用TCP或UDP而抽象出來的一層,是位于應(yīng)用層和傳輸控制層之間的一組接口。tcp是可靠的連接,且連接后才可以發(fā)送數(shù)據(jù);udp是不可靠的連接,不連接就可以發(fā)送數(shù)。

Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口。在設(shè)計模式中,Socket其實就是一個門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。

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

WebSocket則是一個典型的應(yīng)用層協(xié)議,Socket是傳輸控制層協(xié)議。

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

 

2.5 WebSocket 特點以及常見概念

  • 支持瀏覽器/Nodejs環(huán)境
  • 支持雙向通信
  • API簡單易用
  • 支持二進制傳輸
  • 減少傳輸數(shù)據(jù)量

2.5.1 長輪詢

http 長輪詢是服務(wù)器收到請求后如果有數(shù)據(jù), 立刻響應(yīng)請求; 如果沒有數(shù)據(jù)就會 hold 一段時間,這段時間內(nèi)如果有數(shù)據(jù)立刻響應(yīng)請求; 如果時間到了還沒有數(shù)據(jù), 則響應(yīng) http 請求;瀏覽器受到 http 響應(yīng)后立在發(fā)送一個同樣http 請求查詢是否有數(shù)據(jù);

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

 

2.5.2 短輪詢:

客戶端不管是否收到服務(wù)端的response數(shù)據(jù),都會定時向服務(wù)端發(fā)送請求,查詢是否有數(shù)據(jù)更新。 http 短輪詢的局限是實時性低;

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

 

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

長輪詢與短輪詢兩者不同點 間隔發(fā)生在服務(wù)端還是瀏覽器端: http 長輪詢在服務(wù)端會 hold 一段時間, http 短輪詢在瀏覽器端 “hold”一段時間; 應(yīng)用: http長輪詢一般用在 web im, im 實時性要求高, http 長輪詢的控制權(quán)一直在服務(wù)器端, 而數(shù)據(jù)是在服務(wù)器端的,因此實時性高;

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

2.5.3 長連接

  1. 長連接是指的TCP連接,而不是HTTP連接
  2. 長連接意味著連接會被復(fù)用
  3. 服務(wù)器和客戶端都設(shè)置 Connection: keep-alive
  4. 現(xiàn)在基本用的HTTP1.1協(xié)議,HTTP1.1默認長連接

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

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

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

好處:

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

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

注意:因為HTTP1.1協(xié)議以后,連接默認都是長連接。沒有短連接說法(HTTP1.0默認使用短連接),網(wǎng)上大多數(shù)指的長短連接實質(zhì)上說的就是TCP連接。

2.5.4 短連接

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

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

http1.0 不支持 http 長連接, 每次一個 http請求響應(yīng)后都關(guān)閉 tcp 連接, 下個 http 請求會重新建立 tcp 連接.

2.5.5 通訊概念

  • 單工 數(shù)據(jù)傳輸?shù)姆较蛭ㄒ唬荒苡砂l(fā)送方向接收方的單一固定方向傳輸數(shù)據(jù)。
  • 半雙工 即通信雙方既是接收方也是發(fā)送方,不過,在某一時刻只能允許向一個方向傳輸數(shù)據(jù)。
  • 全雙工: 即通信雙方既是接收方也是發(fā)送方,兩端設(shè)備可以同時發(fā)送和接收數(shù)據(jù)。

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

三、HTTP與TCP的區(qū)別和聯(lián)系

3.1、TCP連接

建立起一個TCP連接需要經(jīng)過“三次握手”:

第一次握手:客戶端發(fā)送syn包(syn=j)到服務(wù)器,并進入SYN_SEND狀態(tài),等待服務(wù)器確認;

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

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

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

3.2、HTTP連接

HTTP協(xié)議即超文本傳送協(xié)議(Hypertext Transfer Protocol ),是Web聯(lián)網(wǎng)的基礎(chǔ),也是手機聯(lián)網(wǎng)常用的協(xié)議之一,HTTP協(xié)議是建立在TCP協(xié)議之上的一種應(yīng)用。

HTTP連接最顯著的特點是客戶端發(fā)送的每次請求都需要服務(wù)器回送響應(yīng),在請求結(jié)束后,會主動釋放連接。從建立連接到關(guān)閉連接的過程稱為“一次連接”。

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

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

由于HTTP在每次請求結(jié)束后都會主動釋放連接,因此HTTP連接是一種“短連接”,要保持客戶端程序的在線狀態(tài),需要不斷地向服務(wù)器發(fā)起連接請求。通常的 做法是即時不需要獲得任何數(shù)據(jù),客戶端也保持每隔一段固定的時間向服務(wù)器發(fā)送一次“保持連接”的請求,服務(wù)器在收到該請求后對客戶端進行回復(fù),表明知道客 戶端“在線”。若服務(wù)器長時間無法收到客戶端的請求,則認為客戶端“下線”,若客戶端長時間無法收到服務(wù)器的回復(fù),則認為網(wǎng)絡(luò)已經(jīng)斷開。

  • TCP是底層通訊協(xié)議,定義的是數(shù)據(jù)傳輸和連接方式的規(guī)范
  • HTTP是應(yīng)用層協(xié)議,定義的是傳輸數(shù)據(jù)的內(nèi)容的規(guī)范
  • HTTP協(xié)議中的數(shù)據(jù)是利用TCP協(xié)議傳輸?shù)模灾С諬TTP也就一定支持TCP
  • HTTP支持的是www服務(wù) ;
  • 而TCP/IP是協(xié)議是Internet國際互聯(lián)網(wǎng)絡(luò)的基礎(chǔ)。TCP/IP是網(wǎng)絡(luò)中使用的基本的通信協(xié)議。

四、nodejs創(chuàng)建web服務(wù)器和Tcp服務(wù)器

4.1 使用Express創(chuàng)建Web服務(wù)器

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('服務(wù)再次啟動')
})復(fù)制代碼

4.2 創(chuàng)建TCP服務(wù)器

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

  • 使用net模塊創(chuàng)建TCP服務(wù)器
  • 使用telnet連接TCP服務(wù)器
  • 使用net創(chuàng)建TCP客戶端

下面代碼構(gòu)建一個TCP服務(wù)器:

//引入net模塊
var net=require('net');
//創(chuàng)建TCP服務(wù)器var server=net.createServer(function(socket){
    console.log('someone connets');
})server.listen(18001,function(){
    console.log('server is listening');
});復(fù)制代碼

當在瀏覽器中輸入http://localhost:18001/,連接成功后就會打印出someone connets字樣,表明createServer方法的回調(diào)函數(shù)已經(jīng)執(zhí)行,說明已經(jīng)成功連接這個TCP服務(wù)器。

服務(wù)端:tcp.js:

const HOST = '127.0.0.1';
//引入net模塊var net=require('net');
//創(chuàng)建TCP服務(wù)器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);
     //發(fā)送數(shù)據(jù)     socket.write(message,function(){
        var writeSize=socket.bytesWritten;         console.log(message+'has send');
        console.log('the size of message is'+writeSize);
    });    //監(jiān)聽dada事件    socket.on('data',function(data){
        //打印data        console.log(data.toString());
        socket.write('server write:test ')
    });}); //設(shè)置監(jiān)聽端口server.listen(18001,HOST,function(){  
    console.log('server is listening');
});復(fù)制代碼

啟動服務(wù) node tcp.js

客戶端 cmd

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

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

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

 

2> 或者通過網(wǎng)絡(luò)調(diào)試助手模擬客戶端和服務(wù)端 調(diào)試

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

 

網(wǎng)絡(luò)調(diào)試助手調(diào)試下載地址:

鏈接:https://pan.baidu.com/s/1bZQm1f9UtBKPSAIyqM4Nyw 
提取碼:d9ck
復(fù)制代碼

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

客戶端 tcpClient.js:

var net = require('net')
const PORT = 18001;
const HOST = '127.0.0.1';
var tcpClient = net.Socket();
tcpClient.connect(PORT, HOST, function(){
  console.log('客戶端發(fā)送信息成功打印')
  tcpClient.write('客戶端發(fā)送信息成功')
});tcpClient.on('data',(data)=>{
  console.log( data.toString())
})復(fù)制代碼

五、websocket 常用框架

  • socket.io
  • Ws

5.1 WebSocket事件:

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

5.1.1 open:

一旦服務(wù)端響應(yīng)WebSocket連接請求,就會觸發(fā)open事件。響應(yīng)的回調(diào)函數(shù)稱為onopen。

ws.onopen = function(e) {
    console.log("Connection open...");
};復(fù)制代碼

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

5.1.2 Message

當消息被接受會觸發(fā)消息事件,響應(yīng)的回調(diào)函數(shù)叫做onmessage。如下: // 接受文本消息的事件處理實例:

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);
}};復(fù)制代碼

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

// 設(shè)置二進制數(shù)據(jù)類型為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);}};//ArrayBufferws.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);}};復(fù)制代碼

5.1.3 Error

如果發(fā)生意外的失敗會觸發(fā)error事件,相應(yīng)的函數(shù)稱為onerror,錯誤會導(dǎo)致連接關(guān)閉。如果你收到一個錯誤事件,那么你很快會收到一個關(guān)閉事件,在關(guān)閉事件中也許會告訴你錯誤的原因。而對錯誤事件的處理比較適合做重連的邏輯。

//異常處理
ws.onerror = function(e) {
   console.log("WebSocket Error: " , e);
};復(fù)制代碼

5.1.4 Close

不言而喻,當連接關(guān)閉的時候回觸發(fā)這個事件,對應(yīng)onclose方法,連接關(guān)閉之后,服務(wù)端和客戶端就不能再收發(fā)消息。 當然你可以調(diào)用close方法斷開與服務(wù)端的鏈接來觸發(fā)onclose事件,

ws.onclose = function(e) {
    console.log("Connection closed", e);
};復(fù)制代碼

5.2 WebSocket事件和方法:

5.2.1 send

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

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

注意:必須是open之后才可以send數(shù)據(jù)

var ws = new WebSocket("ws://echo.websocket.org")
ws.onopen = function(e) {
    ws.send("Initial data");
}復(fù)制代碼

如果想通過響應(yīng)別的事件去發(fā)送消息,可以檢查readyState屬性的值為open的時候來實現(xiàn)。

function myEventHandler(data) {
if (ws.readyState === WebSocket.OPEN) {
    //open的時候即可發(fā)送
    ws.send(data);} else {
    // Do something else in this case.
}}復(fù)制代碼

發(fā)送二進制數(shù)據(jù):

// 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);
復(fù)制代碼

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

5.2.2 close()

使用close方法來關(guān)閉連接,如果連接以及關(guān)閉,這方法將什么也不做。調(diào)用close方法只后,將不能發(fā)送數(shù)據(jù)。 ws.close();

六、 websocket服務(wù)端基于ws的搭建

1.簡單安裝

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

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

2.服務(wù)端server.js配置

在項目里面新建一個server.js,創(chuàng)建服務(wù),指定8181端口,將收到的消息log出來。

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);
    });});復(fù)制代碼

服務(wù)端完整代碼如下:

var WebSocket = require('ws').Server;
let wss = new WebSocket({    port:8181
})// wss 繼承eventEmitterwss.on('connection',function(ws){
    //鏈接成功    console.log('建立鏈接')
    // 監(jiān)聽客戶端發(fā)送的信息    ws.on('message',function(data){
        console.log(data);
        //單一:誰給我發(fā), 我就給誰發(fā)        ws.send('hello' + data )
        //群發(fā)     })    ws.on('close',function(){
        console.log('關(guān)閉鏈接')
    })});復(fù)制代碼

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

我們需要通過調(diào)用WebSocket構(gòu)造函數(shù)來創(chuàng)建一個WebSocket連接,構(gòu)造函數(shù)會返回一個WebSocket實例,可以用來監(jiān)聽事件。這些事件會告訴你什么時候連接建立,什么時候消息到達,什么時候連接關(guān)閉了,以及什么時候發(fā)生了錯誤。

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

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

var ws = new WebSocket("ws://localhost:8181");
    ws.onopen = function (e) {
        console.log('Connection to server opened');
    }    function sendMessage() {
        ws.send($('#message').val());
    }復(fù)制代碼

具體完整代碼如下:

  <input class="form-control" type="text" name="message" id="message"
            placeholder="Type text to echo in here" value="" />
            <button type="button" id="send" class="btn btn-primary" onclick="sendMessage();">
            Send!        </button><script>         //原生的 ie9不支持        var ws = new WebSocket("ws://localhost:8181");
            ws.onopen = function (e) {
                console.log('Connection to server opened');
                let msg = {type:'test',id:1,info:'laney'}
                ws.send(JSON.stringify(msg));                console.log("sened a laney");
            }            function sendMessage() {
                var message = document.getElementById('message');
                // ws.send(message.value); //給后端發(fā)送信息                ws.send(JSON.stringify({type:'test1',id:1,info:message.value}))
            }            //收到服務(wù)端的請求            ws.onmessage = function (e) {
                console.log(e.data);
            }            </script>  復(fù)制代碼

運行之后如下,服務(wù)端即時獲得客戶端的消息。

七、websocket服務(wù)端基于socket.io的搭建

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

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

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

7.1 socket.io事件

7.1.1 socket.io服務(wù)器的系統(tǒng)事件

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

7.1.2 客戶端的系統(tǒng)事件

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

7.2 原生 nodejs 結(jié)合 Socket.io 實現(xiàn)服務(wù)器和 客戶端的相互通信

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

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

7.2.1 為什么需要 WebSocket

了解計算機網(wǎng)絡(luò)協(xié)議的人,應(yīng)該都知道:HTTP 協(xié)議是一種無狀態(tài)的、無連接的、單向的應(yīng)用層協(xié)議。它采用了請求/響應(yīng)模型。通信請求只能由客戶端發(fā)起,服務(wù)端對請求做出應(yīng)答處理。

這種通信模型有一個弊端:HTTP 協(xié)議無法實現(xiàn)服務(wù)器主動向客戶端發(fā)起消息。

7.2.2 具體步驟和使用

1、 安裝 Socket.io

網(wǎng)址:socket.io/

npm install socket.io 
或者 yarn add  socket.io
復(fù)制代碼

2、搭建一個server 服務(wù)器, 創(chuàng)建一個 io 對象。

var http = require('http');
var path = require('path');
var fs = require('fs');
//引入socket.iovar socket = require('socket.io');
var app = http.createServer(function(req,res){
     //加載靜態(tài)頁面    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服務(wù)建立連接var io = socket(app);
// 監(jiān)聽連接事件 io.on('connection',function(sock){
    console.log(sock)
    console.log('服務(wù)器建立連接了');
})app.listen(3000,'127.0.0.1');
復(fù)制代碼

socket 與 http服務(wù)建立連接后,你就會發(fā)現(xiàn),http://127.0.0.1:3000/socket.io/socket.io.js 就是一個 js 文件 的地址了。

現(xiàn)在需要制作一個客戶端的 index 頁面,這個頁面中,必須引用秘密 js 文件。調(diào)用 io 函數(shù),取得 socket 對象。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>客戶端</title>
    <script src="http://localhost:3000/socket.io/socket.io.js"></script>
</head>
<body>
        <script>
      var socket = io('http://localhost:3000/');  /*和服務(wù)器建立連接*/
    </script>
</body>
</html>
復(fù)制代碼

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

3、服務(wù)器端通過 emit 廣播,通過 on 接收廣播

//引入socket.iovar socket = require('socket.io');
// socket 與 http服務(wù)建立連接var io = socket(app);
// 監(jiān)聽連接事件 io.on('connection',function(sock){
    console.log('一個客戶端與服務(wù)器建立連接了');
    //服務(wù)器獲取客戶端廣播的數(shù)據(jù)    sock.on('addcart', function(data){
        console.log(data);
          //服務(wù)器給客戶端發(fā)送數(shù)據(jù)            //socket.emit();   /*誰給我發(fā)信息我把信息廣播給誰*/            //io.emit() ;   /*群發(fā)  給所有連接服務(wù)器的客戶都廣播數(shù)據(jù)*/
        socket.emit('to-client','我是服務(wù)器的數(shù)據(jù)'+data.client);
        //io.emit('to-client','我是服務(wù)器的數(shù)據(jù)'+data.client);
    })    sock.on('disconnect',function(){
        console.log('斷開連接了');
    })})復(fù)制代碼

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

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

io.emit('to-client','我是服務(wù)器的數(shù)據(jù)'+data.client);
 /*群發(fā)  給所有連接服務(wù)器的客戶都廣播數(shù)據(jù)*/
復(fù)制代碼

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

 <button id="button">給服務(wù)器發(fā)送數(shù)據(jù)</button>
    <script src="http://localhost:3000/socket.io/socket.io.js"></script>
    <script>
      var socket = io('http://localhost:3000/');  /*和服務(wù)器建立連接*/
      socket.on('connect',function(){
            console.log('客戶端和服務(wù)端建立連接了');
      })
      socket.on('disconnect',function(){
            console.log('客戶端和服務(wù)端斷開連接了');
      })
      var btn=document.getElementById('button');
      btn.onclick= function () {
                //客戶端給服務(wù)器發(fā)送數(shù)據(jù)
                socket.emit('addcart',{
                    client:'我是客戶端的數(shù)據(jù)'
                })
        }
       //監(jiān)聽服務(wù)器的廣播
      socket.on('to-client',function(data){
         console.log('服務(wù)端說:'+data)
       })
    </script>
復(fù)制代碼

7.3 Express 結(jié)合 Socket.io 實現(xiàn)服務(wù)器和客戶 端的相互通信

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

var app = require('express')();
//創(chuàng)建http server ,  createServer 與 Server作用一樣都是創(chuàng)建server
var http = require('http').createServer(app);
//var http = require('http').Server(app);
app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});http.listen(3000, () => {
  console.log('listening on *:3000');
});復(fù)制代碼

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

我們讓http服務(wù)器監(jiān)聽端口3000。

7.3.1 引入 Socket.IO

npm install socket.io

注意:socket.io基于http服務(wù)(socket.io的參數(shù)就是http server)

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');
});復(fù)制代碼

7.3.2 寫socket的代碼

//3、寫socket的代碼
io.on('connection', function (socket) {
      console.log('a user connected');
      socket.on('message',function(key){
          console.log(key);
           //io.emit  廣播
        //socket.emit  誰給我發(fā)的信息我回返回給誰          //io.emit('servermessage',data);   /*服務(wù)器給客戶端發(fā)送數(shù)據(jù)*/
         //   機器人聊天        //   console.log(robot[key])
        //   socket.emit('servermessage',robot[key]);
          io.emit('servermessage',key.msg);
      })  });復(fù)制代碼

7.3.3 群聊

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

io.emit('servermessage',data); /服務(wù)器給客戶端發(fā)送數(shù)據(jù)/

7.3.4 機器人聊天

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

socket.emit('servermessage',robot[key]);
復(fù)制代碼

7.3.5 客戶端 代碼

  <ul id="messages"> </ul>
    <form >
      <input id="msg" autocomplete="off" /><button id="send" >Send</button>
    </form>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
     //群聊功能--聊天室
     $('#send').click(function(e){
         e.preventDefault();
            var msg=$('#msg').val();
            // socket.emit('message',msg);  /*客戶端給服務(wù)器發(fā)送數(shù)據(jù)*/
            // 帶上自己的名字
            socket.emit('message',{
                name:'laney',
                msg:msg
            });  /*客戶端給服務(wù)器發(fā)送數(shù)據(jù)*/
        })
   //接受服務(wù)器返回的數(shù)據(jù)
   socket.on('servermessage',function(data){
        console.log(data)
        $('#messages').append(`<li>${data}<li>`)
    })
</script>


最后,咱給小編:

 

1. 點贊+評論

2. 點頭像關(guān)注,轉(zhuǎn)發(fā)給有需要的朋友。

 

謝謝!!

分享到:
標簽:Websocket
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定