• Python3 如何实现 websocket 服务?


    Python 实现 websocket 服务很简单,有很多的三方包可以用,我从网上大概找到三种常用的包:websocketwebsocketsFlask-Sockets

    但这些包很多都“年久失修”, 比如 websocket2010 年就不维护了。

    在这里插入图片描述

    Flask-Sockets 也在 2016 年停止维护。

    在这里插入图片描述

    这也给我们提了一个醒,用三方包的时候一定要看下这个包是否还在持续维护,如果作者已经停止了维护,那就坚决不要再用了,因为过不了多久你就会吃个大亏的。

    websockets

    排除了两个已经不维护的包,现在只剩下一个 websockets 了,那么这个包会满足我们的需求吗?

    首先看了下 websockets,发现社区最近还在维护,而且 websockets 还有完整的源码和使用教程,对新手非常友好。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    源码和教程地址:
    https://pypi.org/project/websockets/
    https://github.com/python-websockets/websockets
    https://websockets.readthedocs.io/en/stable/intro/tutorial1.html

    示例:

    service.py

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    import asyncio
    import websockets
    
    
    async def hello(websocket):
        recv_data = await websocket.recv()
        print('<<< %s' % recv_data)
    
        send_data = 'Hello %s' % recv_data
        await websocket.send(send_data)
        print('>>> %s' % send_data)
    
    
    async def start():
        print('Server started ...')
        async with websockets.serve(hello, '0.0.0.0', 8765):
            await asyncio.Future()
    
    
    if __name__ == '__main__':
        asyncio.run(start())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    client.py

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    import asyncio
    import websockets
    
    
    async def hello():
        uri = 'ws://0.0.0.0:8765'
        async with websockets.connect(uri) as websocket:
    
            send_data = input("What's your name: ")
    
            await websocket.send(send_data)
            print('>>> %s' % send_data)
    
            recv_data = await websocket.recv()
            print('<<< %s' % recv_data)
    
    
    if __name__ == '__main__':
        asyncio.run(hello())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这样就是个简单的 WebSocket 服务器/客户端程序了,也是 WebSocket 中最核心的东西了。

    运行结果:

    在这里插入图片描述

    在这里插入图片描述

    但是这样有一个问题,服务器在收到一条消息之后就关闭连接,如果想实现持久连接就需要用到循环来处理了。

    async with websockets.connect(uri) as websocket:
        for i in range(10):
            send_data = input("What's your name: ")
            ...
    
    • 1
    • 2
    • 3
    • 4

    如果用到异步需要加上 async

    async def handler(websocket):
        async for message in websocket:
            print(message)
    
    • 1
    • 2
    • 3

    有时候可能还会出现一次只能有一个客户端连接的问题,一般来说,这是程序中时间的调用没有使用异步导致。

    例如,此连接处理程序可阻止事件循环在一秒钟内运行:

    async def handler(websocket):
        time.sleep(1)
        ...
    
    • 1
    • 2
    • 3

    将其更改为:

    async def handler(websocket):
        await asyncio.sleep(1)
        ...
    
    • 1
    • 2
    • 3

    如何开启多进程?

    如果想开启多进程,可以用 Python 自带的包实现,先导入进程池模块,然后启动所有进程,多进程用法参考 Python3 多进程编程 这篇文章。

    from multiprocessing import Pool
    
    ...
    
    def main():
        asyncio.run(start())
    
    if __name__ == '__main__':
        p = Pool(30)
        for i in range(10):
            p.apply_async(main)
    
        p.close()
        p.join()
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如何开启 SSL/TLS 服务?

    service.py

    import ssl
    
    certfile = 'fullchain.pem'
    keyfile = 'privkey.pem'
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ssl_context.load_cert_chain(certfile, keyfile)
    
    ...
    
    async def start():
        print('Server started ...')
        async with websockets.serve(hello, '0.0.0.0', 8765, ssl=ssl_context):
            await asyncio.Future()
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    client.py

    import ssl
    
    certfile = 'fullchain.pem'
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ssl_context.load_verify_locations(certfile)
    
    ...
    
    async def hello():
        uri = 'wss://0.0.0.0:8765'
        async with websockets.connect(uri, ssl=ssl_context) as websocket:
    
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果是自签名的话客户端需要 TLS 上下文。如果采用的是 CA 签署的有效证书,连接到安全 WebSocket 服务器时,可以直接建简化参数 ssl=True。如果开启了 SSL/TLS 服务,访问 WebSocket 服务器时就需要使用 wss 协议了。

    使用 Supervisor 和 Nginx 部署 Websocket 服务

    supervisor 配置:

    [program:websocket]
    directory=/opt/app/script
    command=/usr/bin/python3 /opt/app/script/ws_service.py
    process_name = %(program_name)s_%(process_num)02d
    numprocs = 30
    stderr_logfile=/tmp/websocket_stderr.log
    stdout_logfile=/tmp/websocket_stdout.log
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Nginx 配置:

    server {
    
    	listen       80;
        listen       443 ssl;
        server_name  ws.xxx.com;
    
        ssl on;
        ssl_certificate     /opt/ssl_cert/fullchain.pem;
        ssl_certificate_key /opt/ssl_cert/privkey.pem;
        ssl_session_timeout 5m;
        ssl_session_cache shared:SSL:50m;
        ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    
        location / {
    
            proxy_pass http://xxx.xxx.xxx.xxx:8765;
    
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    更多技巧和常见问题请参考官方文档:https://websockets.readthedocs.io/en/stable/faq/server.html

    有关 SSL/TLSWS/WSS 的知识请参考以下文章:
    HTTP/HTTPS、SSL/TLS、WS/WSS 都是什么?

    后端 Service 端设置 SSL 访问参考以下文章:
    python websocket ssl server
    Python Websockets SSL with Let’s Encrypt

    前端 Vue client 如何接收 Service 端数据可以参考以下文章:
    前端如何使用websocket获取后端数据
    vue中使用websocket通信接收后台数据
    vue封装websocket请求项目实战(完整版)

    Nginx 部署 Websocket 服务参考以下文章:
    https://ningyu1.github.io/site/post/56-websocket-ssl

  • 相关阅读:
    个人主页汇总 | 私信没空看,建议b站
    linux配置IP、子网掩码、网关
    【C++项目】高并发内存池第五讲内存回收释放过程介绍
    客户分析中变量分类
    SElinux 导致 Keepalived 检测脚本无法执行
    如何退出commit_message页面
    “/home/test/cc/bk-server/docker-compose.yml“ docker配置
    【数据结构】建立二叉树以及哈夫曼树及哈夫曼编码
    10.7- 的报错整理(与linux、python相关)
    Linux网路服务之“PXE网络批量装机和Kickstart全自动化安装”
  • 原文地址:https://blog.csdn.net/yilovexing/article/details/133124547