作者:小小明

在前面的用Tornado实现web聊天室一文中介绍了python实现websocket的方法,这篇文章将要分享如何用python作为客户端获取websocket接口的数据。

前文链接:https://blog.csdn.net/as604049322/article/details/112386560

websocket的使用

WebSocket 是一种在单个 TCP/TSL 连接上,进行全双工、双向通信的协议。WebSocket 可以让客户端与服务器之间的数据交换变得更加简单高效,服务端也可以主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行双向数据传输。

WebSocket 可以在连续发送数据的同时不断地接受消息。并不会像 REST 一样,每发送一个请求,要等待服务器完成请求、完全回复之后,再进行下一个请求。”全双工“可以理解为在请求的同时也可以接受消息

websocket与传统http协议的对比:

image-20210124212923278

websocket服务端

首先启动一个简单的websocket服务端用于测试,代码如下:

__author__ = 'xiaoxiaoming'

import datetime
from abc import ABC

import tornado.httpserver
import tornado.ioloop
import tornado.web
from tornado.options import define, options
from tornado.websocket import WebSocketHandler

define("host", default="127.0.0.1", type=str)
define("port", default=8000, type=int)


class ChatHandler(WebSocketHandler, ABC):
    users = set()  # 用来存放在线用户的容器

    def open(self):
        self.users.add(self)  # 建立连接后添加用户到容器中
        for u in self.users:  # 向已在线用户发送消息
            u.write_message(
                f"[{self.request.remote_ip}]-[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]-进入聊天室")

            def on_message(self, message):
                for u in self.users:  # 向在线用户广播消息
                    u.write_message(u"[%s]-[%s]-说:%s" % (
                        self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), message))

            def on_close(self):
                self.users.remove(self)  # 用户关闭连接后从容器中移除用户
                for u in self.users:
                    u.write_message(
                        f"[{self.request.remote_ip}]-[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]-离开聊天室")

            def check_origin(self, origin):
                return True  # 允许WebSocket的跨域请求

        if __name__ == '__main__':
            app = tornado.web.Application([
                (r"/", ChatHandler),
            ])
            http_server = tornado.httpserver.HTTPServer(app)
            http_server.listen(options.port)
            print(f"http://{options.host}:{options.port}")
            tornado.ioloop.IOLoop.current().start()

以上代码依赖于tornado,没有安装的需要使用pip安装:

pip install tornado

JavaScript的websocket客户端

websocket的客户端使用JavaScript会非常简单,只需要在游览器后台执行:

var ws = new WebSocket("ws://127.0.0.1:8000/"); // 新建一个ws连接
ws.onmessage = function (evt) {  // 收到服务器发送的消息后执行的回调
   console.log(evt.data);  // 接收的消息内容在事件参数evt的data属性中
};

即可在游览器连接上websocket服务端,并在获得消息时自动控制台显示。

执行以下命令可向服务端发送消息:

ws.send("xxxx")

在运行上面的服务端后,我们在游览器中执行以上的JavaScript代码:

image-20210124203721550

服务端只是简单把从客户端收到的所有的消息,加上ip和时间发送给所有的客户端。从上面的结果可以看到我们的测试服务端顺利运行。

Python的websocket同步客户端

那么python中如何实现这样的客户端呢?代码如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 创建时间:2021/1/24 17:06
__author__ = 'xiaoxiaoming'

import time

import websocket
import _thread


# 在接收到服务器发送消息时调用
def on_message(ws, message):
    print('Received: ' + message)


# 在和服务器建立完成连接时调用
def on_open(ws):
    # 线程运行函数
    def process():
        while True:
            s = input("要发送的内容(quit表示退出):")
            if s == "quit":
                break
            ws.send(s)
            # 休息 0.2 秒先接收服务器回复的消息
            time.sleep(0.2)
        # 关闭 Websocket 的连接
        ws.close()
        print("Websocket closed")

    # 在另一个线程运行 gao() 函数
    _thread.start_new_thread(process, ())


if __name__ == "__main__":
    ws = websocket.WebSocketApp("ws://127.0.0.1:8000/",
                                on_message=on_message,
                                on_open=on_open)

    ws.run_forever()

上面的代码on_open方法启动了一个用于向服务端发送消息的线程。

运行后,也可以顺利看到执行效果:

image-20210124204540219

如果上面已经连接的预览器没有关闭的话也可以收到消息:

image-20210124204819420

需要注意的是这个客户端依赖的包是websocket_client,而不是websocket,如果你缺少这个库,需要执行:

pip -m install websocket_client

来安装。

Python的websockets异步客户端

python支持websocket客户端除了上面这种同步接口,还提供了websockets这种协程实现的异步接口,在我们不需要使用input这种阻塞式方法时,建议直接使用websockets。

需要以下命令来安装:

pip install websockets

测试代码:

__author__ = 'xiaoxiaoming'

import asyncio

import websockets


async def process():
    async with websockets.connect("ws://127.0.0.1:8000/") as ws:
        while True:
            try:
                message = await asyncio.wait_for(ws.recv(), timeout=30)
                print('Received: ' + message)
            except asyncio.TimeoutError as e:
                continue
            except websockets.exceptions.ConnectionClosed as e:
                print('连接已经关闭')
                break

asyncio.run(process())

如果是python3.7以下的版本:

asyncio.run(process())

需要更换为:

loop = asyncio.get_event_loop()
loop.run_until_complete(process())
loop.close()

当然建议直接使用python3.7以上版本的协程。

运行后可以顺利的收到从服务端发来的消息:

image-20210125021724900


本文转载:CSDN博客