Websocket

Websocket模塊

WebSocket 是什麼?

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

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

爲何須要 WebSocket ?

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

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

這種單向請求的特色,註定了若是服務器有連續的狀態變化,客戶端要獲知就很是麻煩。大多數 Web 應用程序將經過頻繁的異步 JavaScript 和 XML(AJAX)請求實現長輪詢。輪詢的效率低,很是浪費資源(由於必須不停鏈接,或者 HTTP 鏈接始終打開)。web

所以,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。WebSocket 鏈接容許客戶端和服務器之間進行全雙工通訊,以便任一方均可以經過創建的鏈接將數據推送到另外一端。WebSocket 只須要創建一次鏈接,就能夠一直保持鏈接狀態。這相比於輪詢方式的不停創建鏈接顯然效率要大大提升。json

WebSocket 如何工做?

Web 瀏覽器和服務器都必須實現 WebSockets 協議來創建和維護鏈接。因爲 WebSockets 鏈接長期存在,與典型的 HTTP 鏈接不一樣,對服務器有重要的影響。flask

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

WebSocket 客戶端

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

客戶端 API

如下 API 用於建立 WebSocket 對象。服務器

var Socket = new WebSocket(url, [protocol] );

Websocket屬性

屬性 描述
Socket.readyState 只讀屬性readySate表示鏈接狀態,能夠是如下值:0 - 表示鏈接還沒有創建,1 - 表示鏈接已創建,能夠進行通訊. 2 - 表示鏈接正在進行關閉.3 - 表示鏈接已經關閉或者鏈接不能打開
Socket.bufferedAmount 只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,可是尚未發出的 UTF-8 文本字節數。

WebSocket事件

事件 事件處理程序 描述
open Socket.onopen 鏈接創建時觸發
message Socket.onmessage 客戶端接收服務端數據時觸發
error Socket.onerror 通訊發生錯誤時觸發
close Socket.onclose 鏈接關閉時觸發

WebSocket方法

方法 描述
Socket.send() 使用鏈接發送數據
Socket.close() 關閉鏈接

gevent-websocket模塊(python操做websocket)

模擬羣聊小示例

# views.py
from geventwebsocket.handler import WebSocketHandler
from geventwebsocket.server import WSGIServer
from geventwebsocket.websocket import WebSocket   # 用於提示語法信息

from flask import Flask, request, render_template

app = Flask(__name__)

socket_list = []

@app.route('/ws')
def ws():
    sock = request.environ.get('wsgi.websocket')   # type:WebSocket
    # request.environ是個大字典,獲取wsgi.websocket鍵對應的值即得到web.socket對象
    socket_list.append(sock)
    
    while True:
        try:
            msg = sock.receive()   #  receive v.接收
        except:
            socket_list.remove(sock)
            break
        # 監聽時若是鏈接異常會報錯,異常捕捉若是接收異常則刪除這個websocket對象
        for so in socket_list:
            if so == sock:
                continue
            try:
                so.send(msg)
            except:
                continue
            # 若是發送的客戶端斷開則會報錯,捕捉異常,若是沒法正常發送則跳過
    return '200 ok'

@app.route('/')
def index():
    return render_template('wb_client.html')

if __name__ == '__main__':
    http_serv = WSGIServer(('0.0.0.0', 9527), app, handler_class=WebSocketHandler)
    # app 是Flask示例化對象,wsgi處理完environ以後交給Flaskrequest處理.handler_class處理類型.
    http_serv.serve_forever()
websocket.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3個meta標籤*必須*放在最前面,任何其餘內容都*必須*跟隨其後! -->
    <title>思宇羣聊</title>
</head>
<body>
    <input type="text" id="text">
    <button id="send">點擊發送消息</button>
    <div id="content_list"></div>
<script type='application/javascript'>
    var ws = new WebSocket('ws://192.168.14.118/ws')
    # 回調函數
    # 鏈接創建時調用
    ws.onopen = function() {
        ws.send('hello')
    }
    # 接收到消息時,執行
    ws.onmessage = function (eventMessage) {
        var s = eventMessage.data;  # 獲取數據
        var p = document.createElement('p')
        p.innerText = s;
        document.getElementById('content_list').appendChild(p);
    };
    # 綁定點擊事件發送消息
    document.getElementById('send').addEventListener('onclick', function()                   {
        var st = document.getElementById('text').value;
        ws.send(st);
    })
</script>
</body>
</html>

模擬點對點小示例

# views.py

import json

from geventwebsocket.handler import WebSocketHandler # ws 協議請求處理
from geventwebsocket.server import WSGIServer        # 替換Flask原有的WSGI
from geventwebsocket.websocket import WebSocket

from flask import Flask, request, render_template

app = Flask(__name__)
socket_dict = {}

@app.route('/ws/<username>')
def ws(username):
    sock = request.environ.get('wsgi.websocket', None)  # type: WebSocket
    # 1.不是WS協議上的請求,則得到None
    # 2.WS協議認證失敗,得到None
    if not sock:
        return '請使用WS協議鏈接'
    socket_dict[username] = sock
    
    while True:
        msg = sock.receive()
        msg_dickt = json.loads(msg)
        recv = msg_dict.get('recv')
        recv_sock = socket_dict.get(recv)
        recv_sock.send(msg)

@app.route('/')
def index():
    return render_template('ws_client_2.html')
if __name__ == '__main__':
    http_serv = WSGIServer(('0.0.0.0', 9527),app, handler_class = WebSocketHandler)
    http.serv.serve.forever()
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap 101 Template</title>
</head>
<body>
你的名字:<input type="text" id="username"><button id="createws">登陸聊天室</button>
<p>給<input type="text" id="recv">發送<input type="text" id="text">內容 <button id="send">點擊發送消息</button></p>
<div id="content_list">
</div>
</body>
<script type="application/javascript">
    var ws = null;
    document.getElementById('createws').onclick = function() {
        # 獲取用戶名
        var username = document.getElementById('username').value;
        ws = new WebSocket('ws://192.168.14.26:9527/ws/'+username);
        ws.onopen = function () {
          alert('歡迎進入九聊');  
        };

    ws.onmessage = function (eventMessage) {
        var msg = JSON.parse(eventMessage.data);
        console.log(msg);
        var p = document.createElement('p');
        p.insertText = msg.sender + ':' + msg.msg;
        document.getElementById('content_list').appendChild(p);
    };
};
</script>
</html>
WebSocket協議
目前Workerman的WebSocke協議版本爲13

WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通訊

WebSocket與TCP關係
WebSocket和HTTP同樣是一種應用層協議,都是基於TCP傳輸的,WebSocket自己和Socket並無多大關係,更不能等同。

WebSocket協議握手
WebSocket協議有一個握手的過程,握手時瀏覽器和服務端是以HTTP協議通訊的,在Workerman中能夠這樣介入到握手過程。
相關文章
相關標籤/搜索