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())

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())

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

运行结果:

在这里插入图片描述

在这里插入图片描述

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

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

如果用到异步需要加上 async

async def handler(websocket):
    async for message in websocket:
        print(message)

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

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

async def handler(websocket):
    time.sleep(1)
    ...

将其更改为:

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

如何开启多进程?

如果想开启多进程,可以用 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()
    ...

如何开启 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()
...

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:

...

如果是自签名的话客户端需要 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

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;
    }
}

更多技巧和常见问题请参考官方文档: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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值