websocket原理

內容:css

1.websocket介紹html

2.websocket原理前端

3.websocket實例node

 

參考:http://www.javashuo.com/article/p-nmcskqka-be.htmlgit

 

 

 

1.websocket介紹github

(1)什麼是WebSocketweb

WebSocket 是一種網絡通訊協議。RFC6455 定義了它的通訊標準。後端

WebSocket 是 HTML5 開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。瀏覽器

另外基於多線程或多進程的服務器沒法適用於 WebSockets,由於它旨在打開鏈接,儘量快地處理請求,而後關閉鏈接。任何實際的 WebSockets 服務器端實現都須要一個異步服務器服務器

 

(2)websocket的特色

  • 雙向
  • 性能高

 

 

2.websocket原理

(1)websocket在服務端和客戶端

在客戶端,沒有必要爲 WebSockets 使用 JavaScript 庫。實現 WebSockets 的 Web 瀏覽器將經過 WebSockets 對象公開全部必需的客戶端功能(主要指支持 Html5 的瀏覽器)

而WebSocket 在服務端的實現很是豐富。Node.js、Java、C++、Python 等多種語言都有本身的解決方案

node中:

而Java 的 web 通常都依託於 servlet 容器(Tomcat、Jetty、Resin),此外Spring框架對 WebSocket 也提供了支持

 

(2)websocket核心原理

關於websocket:

  • 創建在 TCP 協議之上,服務器端的實現比較容易。
  • 與 HTTP 協議有着良好的兼容性。默認端口也是80和443,而且握手階段採用 HTTP 協議,所以握手時不容易屏蔽,能經過各類 HTTP 代理服務器。
  • 數據格式比較輕量,性能開銷小,通訊高效。
  • 能夠發送文本,也能夠發送二進制數據。
  • 沒有同源限制,客戶端能夠與任意服務器通訊。
  • 協議標識符是ws(若是加密,則爲wss),服務器網址就是 URL

底層協議包裝:

 

(3)HTTP 和 WebSocket 有什麼關係?

Websocket 實際上是一個新協議,跟 HTTP 協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已,也就是說它是 HTTP 協議上的一種補充

而HTTP2.0(將來推行,如今還未推行)中其實要實現全雙工通訊,故也有人說websocket最終將被HTTP2.0替代

 

(4)WebSocket屬性

WebSocket是前臺的東西,是HTML5帶的一種東西:

  • 只有前臺有WebSocket這個東西
  • 後臺沒有,後臺有Socket

 

 

3.websocket實例

(1)原生websocket

雖然原生的websocket實現起來麻煩,寫起來麻煩,可是咱們仍是要對其有所瞭解

 1 原生WebSocket:
 2   i.net模塊
 3   ii.流程
 4     a.握手
 5       C:version:1三、sec-websocket-key:xxxxx、sha1(key+mask)=>base64
 6       S:101 Switching Protocols、sec-websocket-accept: base64
 7       C <-> S
 8 
 9       Client:
10       onopen
11       onmessage
12       onclose
13 
14       Server:
15       net.createServer(sock=>{});
16       sock.once('data', 握手);
17       sock.on('data', 數據請求);
18       sock.on('end');
19 
20     b.數據幀解析

 

原生websocket使用實例以下:

 1 // 服務端
 2 const http = require('http');         // HTTP    非原生Socket
 3 const net = require('net');           // TCP     原生Socket
 4 const crypto = require('crypto');
 5 
 6 /*
 7 let server=http.createServer((req, res)=>{
 8   console.log('鏈接');
 9 });
10 server.listen(8080);
11 */
12 
13 let server = net.createServer(sock => {
14     console.log('鏈接');
15 
16     //數據過來 — 握手只有一次
17     sock.once('data', function(data){
18         console.log('hand shake start...');
19 
20         // 下面都是解析HTTP頭
21         let str = data.toString();
22         let lines = str.split('\r\n');
23 
24         //捨棄第一行和最後兩行
25         lines = lines.slice(1, lines.length - 2);
26         //切開headers中的每一項key-value數據
27         let headers = {};
28         lines.forEach(function(line) {
29             let [key, val] = line.split(': ');
30             headers[key.toLowerCase()] = val;
31         });
32         console.log(headers);
33 
34         if (headers['upgrade'] !== 'websocket') {
35             console.log('其餘協議: ', headers['upgrade']);
36             sock.end();
37         } else if (headers['sec-websocket-version'] !== '13') {
38             console.log('版本不對', headers['sec-websocket-version']);
39             sock.end();
40         } else {
41             let key = headers['sec-websocket-key'];
42             let mask = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
43 
44             //sha1(key+mask)->base64=>client
45             let hash = crypto.createHash('sha1');
46             hash.update(key + mask);
47             let key2 = hash.digest('base64');
48             sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${key2}\r\n\r\n`);
49 
50             console.log('hand shake end');
51 
52             //真正的數據
53             sock.on('data', data => {
54                 console.log('有數據');
55                 console.log(data);
56             });
57         }
58     });
59 
60     //斷開了
61     sock.on('end', () => {
62         console.log('客戶端已斷開');
63     });
64 });
65 server.listen(8080);
 1 // 客戶端
 2 <!DOCTYPE html>
 3 <html>
 4 <head>
 5     <meta charset="utf-8">
 6     <title></title>
 7     <script>
 8         let sock=new WebSocket('ws://localhost:8080/');
 9 
10         // 原生websocket中沒有sock.emit 本身用sock.send封裝一個sock.emit:
11         sock.emit=function (name, ...args){
12             alert(JSON.stringify({name, data: [...args]}));
13             sock.send(JSON.stringify({name, data: [...args]}));
14         };
15 
16         // 連上了
17         sock.onopen=function (){
18             alert('鏈接上了');
19 
20             sock.emit('msg', 12, 5);
21         };
22 
23         // 有數據
24         sock.onmessage=function (){
25             alert('有消息過來');
26         };
27 
28         // 斷開了
29         sock.onclose=function (){
30             alert('斷了');
31         };
32     </script>
33 </head>
34 <body>
35 
36 </body>
37 </html>

 

(2)socket.io實現websocket 

實現套路:

 1 // 服務端:
 2 const http = require('http')
 3 const io = require('socket.io')
 4 
 5 let httpServer = http.createServer(function (req, res) {
 6     
 7 })
 8 httpServer.listen(8080)
 9 
10 let wsServer = io.listen(httpServer)
11 
12 wsServer.on('connection', function (sock) {
13     // sock.emit()  -->  發送
14     // sock.on()     -->  接收 
15     // sock.on('connection')  -->  開始鏈接
16     // sock.on('disconnect')  -->  斷開鏈接 
17 })
1 // 客戶端:
2 let sock = io.connect('ws://localhost:8080/')
3 
4 sock.emit()    // 發送
5 sock.on()      // 接收
6 sock.on('connect')      //  開始鏈接
7 sock.on('disconnect')   //  斷開鏈接

 

 

(3)socket.io實現聊天室

  1 // 前端代碼:
  2 <!-- author: wyb -->
  3 <!DOCTYPE html>
  4 <html lang="en">
  5 <head>
  6     <meta charset="UTF-8">
  7     <meta name="viewport" content="width=device-width, initial-scale=1">
  8     <title>聊天室</title>
  9     <style>
 10         *{
 11             margin: 0;
 12             padding: 0;
 13         }
 14         .container{
 15             width: 93%;
 16             margin: 0 auto;
 17             padding: 15px;
 18         }
 19         .err_box{
 20             width: 100%;
 21             height: 20px;
 22             line-height: 20px;
 23             text-align: center;
 24             color: red;
 25             display: none;
 26         }
 27         #u1{
 28             width: 100%;
 29             height: 360px;
 30             border: 1px solid black;
 31             overflow: auto;
 32             margin: 15px 0;
 33         }
 34         #u1 li.me{
 35             color: green;
 36         }
 37         #form{
 38             width: 100%;
 39             text-align: center;
 40             margin: 15px 0;
 41         }
 42         #form textarea{
 43             width: 100%;
 44             min-height: 130px;
 45             margin: 10px 0;
 46         }
 47     </style>
 48     <link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css"
 49           integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
 50     <script src="http://localhost:8080/socket.io/socket.io.js"></script>
 51     <script>
 52         let sock = io.connect('ws://localhost:8080/')
 53 
 54         sock.on('connect', function () {
 55             console.log('已鏈接')
 56             document.querySelector('.err_box').style.display = "none"
 57         })   
 58         sock.on('disconnect', function () {
 59             console.log('已斷開')
 60             document.querySelector('.err_box').style.display = "block"
 61         })   
 62         
 63         window.onload = function(){
 64             let oTxt = document.querySelector('#txt1')
 65             let oBtn = document.querySelector('#btn1')
 66             let oUl = document.querySelector('#u1')
 67 
 68             oBtn.onclick = function () {
 69                 sock.emit('msg', oTxt.value)
 70 
 71                 // 把本身輸入的信息添加到本身頁面中並將顏色設置爲綠色
 72                 let oLi = document.createElement('li')
 73                 oLi.innerHTML = oTxt.value
 74                 oLi.className = 'me'
 75 
 76                 oTxt.value = ''
 77                 oUl.appendChild(oLi)
 78             }
 79 
 80             sock.on('msg', function (str) {
 81                 let oLi = document.createElement('li')
 82                 oLi.innerHTML = str
 83                 oUl.appendChild(oLi)
 84             })
 85 
 86         }
 87 
 88         // 聊天室
 89         // sock.emit()
 90         // sock.on()
 91 
 92     </script>
 93 
 94 </head>
 95 <body>
 96 
 97     <div class="container">
 98         <div class="err_box">沒法鏈接到服務器,請檢查網絡</div>
 99         <ul id="u1"></ul>
100         <form class="pure-form" id="form">
101             <textarea class="pure-input-1" id="txt1" name="" placeholder="請輸入聊天內容"></textarea> <br>
102             <input type="button" value="發送" id="btn1" class="pure-button pure-button-primary">
103         </form>
104     </div>
105 </body>
106 </html>

 

 1 // 後端代碼
 2 const http = require('http')
 3 const io = require('socket.io')
 4 
 5 let httpServer = http.createServer(function (req, res) {
 6     
 7 })
 8 httpServer.listen(8080)
 9 
10 let wsServer = io.listen(httpServer)
11 
12 let aSock = []
13 wsServer.on('connection', function (sock) {
14     aSock.push(sock)
15     console.log(aSock.length)
16 
17     // sock.emit()
18     sock.on('msg', function (str) {
19         aSock.forEach(function (s) {
20             if(s!==sock){
21                 s.emit('msg', str)
22             }
23         })
24     })
25 
26     // 斷開鏈接
27     sock.on('disconnect', function () {
28         let n = aSock.indexOf(sock)
29 
30         if(n!==-1){
31             aSock.splice(n, 1)
32         }
33     })
34 })
35 
36 // 每隔0.5秒打印鏈接數
37 setInterval(function () {
38     console.log(aSock.length)
39 }, 500)

使用方法:啓動服務器後,在瀏覽器中打開多個前端頁面便可開始使用

相關文章
相關標籤/搜索