使用Python創建websocket服務和客戶端請求

1 WebSocket是啥

本段來自 菜鳥教程-WebSocket
WebSocket 和HTTP一樣,也是一種通訊協議,允許服務端主動向客戶端推送數據。
在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。

有很多網站爲了實現推送技術,所用的技術都是 Ajax 輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然後由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。

HTML5 定義的 WebSocket 協議,能更好的節省服務器資源和帶寬,並且能夠更實時地進行通訊。

2 WebSocket的C/S架構

在這裏插入圖片描述
在服務端啓動接收WebSocket請求的服務,客戶端建立Websocket連接併發送請求(Message),服務端接收後,就可以根據處理邏輯,按需向客戶端發送消息了,例如發送主動推送。

3 Python websockets庫

Python websockets是用於在Python中構建WebSocket服務器和客戶端的庫,它基於asyncio異步IO建立,提供基於協程的API。
請儘量使用Python≥3.6以上版本來運行websockets。
安裝websockets:

pip3 install websockets

主要用到的API有:

websockets.connect()
websockets.send()
websockets.recv()

4 簡單例子

server.py,用於構建websocket服務器,在本地8765端口啓動,會將接收到的消息加上I got your message:返回回去。

import asyncio
import websockets


async def echo(websocket, path):
    async for message in websocket:
        message = "I got your message: {}".format(message)
        await websocket.send(message)


asyncio.get_event_loop().run_until_complete(websockets.serve(echo, 'localhost', 8765))
asyncio.get_event_loop().run_forever()

client.py和指定url建立websocket連接,併發送消息,然後等待接收消息,並將消息打印出來。

import asyncio
import websockets


async def hello(uri):
    async with websockets.connect(uri) as websocket:
        await websocket.send("Hello world!")
        recv_text = await websocket.recv()
        print(recv_text)


asyncio.get_event_loop().run_until_complete(
    hello('ws://localhost:8765'))

先執行server.py,再執行client.py,client.py的輸出結果如下:

I got your message: Hello world!

5 主動發消息

建立連接之後,客戶端可以隨時接收服務器發來的消息。服務器可以依據邏輯,給客戶端推送指定消息。
服務器和客戶端代碼會有一點變化,在服務器回完第一條消息之後,開始輪詢時間,當秒數達到0的時候,會主動給客戶端回一條消息。
server.py:

import asyncio
import websockets
import time


async def echo(websocket, path):
    async for message in websocket:
        message = "I got your message: {}".format(message)
        await websocket.send(message)

        while True:
            t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            if str(t).endswith("0"):
                await websocket.send(t)
                break


asyncio.get_event_loop().run_until_complete(
    websockets.serve(echo, 'localhost', 8765))
asyncio.get_event_loop().run_forever()

client.py:

import asyncio
import websockets


async def hello(uri):
    async with websockets.connect(uri) as websocket:
        await websocket.send("Hello world!")
        print("< Hello world!")
        while True:
            recv_text = await websocket.recv()
            print("> {}".format(recv_text))


asyncio.get_event_loop().run_until_complete(
    hello('ws://localhost:8765'))

先執行server.py,再執行client.py,client.py的輸出結果如下:

< Hello world!
> I got your message: Hello world!
> 2020-05-29 15:11:50

最後一條消息則是服務端主動給客戶端發送的。

6 在瀏覽器上使用

如何在前端發送websocket請求呢?
看這段代碼,先建立連接,然後向服務端發送Hello world,然後把接收到的所有消息依次展示出來。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通信客戶端</title>
    <script src="jquery-3.5.0.min.js"></script>
    <script type="text/javascript">
        function WebSocketTest() {
            if ("WebSocket" in window) {
                // 打開一個 web socket
                var ws = new WebSocket("ws://127.0.0.1:8765");
                // 連接建立後的回調函數
                ws.onopen = function () {
                    // Web Socket 已連接上,使用 send() 方法發送數據
                    ws.send("Hello world!");
                    $("#main").append("<p>" + "<=" + "Hello world!" + "</p>")
                };

                // 接收到服務器消息後的回調函數
                ws.onmessage = function (evt) {
                    var received_msg = evt.data;
                    if (received_msg.indexOf("sorry") == -1) {
                        $("#main").append("<p>" + "=>" + received_msg + "</p>")
                    }
                };
                // 連接關閉後的回調函數
                ws.onclose = function () {
                    // 關閉 websocket
                    alert("連接已關閉...");
                };
            } else {
                // 瀏覽器不支持 WebSocket
                alert("您的瀏覽器不支持 WebSocket!");
            }
        }
    </script>
</head>
<body onload="WebSocketTest()">
<div id="main">
</div>
</body>
</html>

效果大概的這樣的:
在這裏插入圖片描述
可以在一開始的時候就抓websocket的包:
在這裏插入圖片描述
這裏可以清晰的看到每一條消息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章