python-socketio

Socket.IO is a transport protocol that enables real-time bidirectional event-based communication between clients (typically, though not always, web browsers) and a server. The official implementations of the client and server components are written in JavaScript. This package provides Python implementations of both, each with standard and asyncio variants.

Socket.IO是一种传输协议,用于 服务器与客户端之间的real-time实时通讯的.是用js写的,python-socketio软件包提供了两种Python的实现方式.一种是异步的一种标准的,但是异步这玩意更新的不完善,有很多坑,所以我们这里还是用标准的.

Quick 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it.
# 这段摘自官方文档介绍,Evenlet是一个Python的基于携程的网络库,它改变了你代码运行的方式,但是没有改变你怎么写代码
# eventlet 一个是处理和网络相关的,另一个可以通过协程实现并发
# 随eventlet一起部署的Socket.IO服务器可以访问长轮询和WebSocket传输。
import eventlet

# Eventlet提供的monkey_patch()功能可以用等效的异步版本替换标准库中的所有阻塞功能。尽管python-socketio不需要猴子补丁,但其他类库(例如数据库驱动程序)也可能需要它
eventlet.monkey_patch()

import socketio

# 实例化服务器
# cors_allowed_origins 跨域请求
# logger,engineio_logger 日志输出方便查看
sio = socketio.Server(logger=True, engineio_logger=True,cors_allowed_origins='*')
# 用一个WSGI应用程序封装
app = socketio.WSGIApp(sio)

# connect跟disconnect 是特殊的两个事件,连接或断开时会自动调用
@sio.event
def connect(sid, environ):
print('connect ', sid)

# 定义事件,有两种方式一种是用函数名另一种就是自定义的
# @sio.on('my custom event')
@sio.event
def my_message(sid, data):
print('message ', data)


# 接收客户端发来的消息
@sio.on('my response')
def response(sid,data):
print(sid)
print(data)

# connect跟disconnect 是特殊的两个事件,连接或断开时会自动调用
@sio.event
def disconnect(sid):
print('disconnect ', sid)

if __name__ == '__main__':
SERVER_ADDRESS = ('127.0.0.1',8000)
sock = eventlet.listen(SERVER_ADDRESS)
eventlet.wsgi.server(sock, app)

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socketio

# 实例化
sio = socketio.Client()

# 默认连接时执行
@sio.event
def connect():
print('connection established')

# 监听事件
@sio.event
def my_message(data):
print('message received with ', data)
# 发送事件sio.emit(对方事件名,data)
sio.emit('my response', {'response': 'my response'})

# 默认断开时执行
@sio.event
def disconnect():
print('disconnected from server')

# 连接
sio.connect('http://127.0.0.1:8000')
sio.wait()

客户端跟服务端搭建好了,那么怎么如何通信呢?

我们可以通过

1
2
3
4
# 发送事件sio.emit(对方事件名,data,房间号) 服务端房间号不写的话是群发给客户端
sio.emit('my response', {'response': 'my response'},room=sid)
# emit 发送事件
# send 相当于发送一个message事件,而且是群发的

使用消息队列

Redis

要使用Redis消息队列,必须安装Python Redis客户端

1
2
3
4
5
6
pip install redis

# socketio.Server class
# 配置redismanager
mgr = socketio.RedisManager('redis://')
sio = socketio.Server(client_manager=mgr)

RabbitMQ

1
2
3
4
5
# 需要安装kombu
pip install kombu

mgr = socketio.KombuManager('amqp://')
sio = socketio.Server(client_manager=mgr)

从外部发送消息

1
2
3
4
5
6
7
# connect to the redis queue as an external process

external_sio = socketio.RedisManager('redis://', write_only=True)

# emit an event
external_sio.emit('my event', data={'foo': 'bar'}, room='my room')

消息队列示例

条件有限,我们这里用redis

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 服务端其实就起到一个将客户端加入房间的作用


import eventlet
eventlet.monkey_patch()

import socketio

mgr = socketio.RedisManager('redis://127.0.0.1:6379/0')

# 实例化服务器
sio = socketio.Server(client_manager=mgr, logger=True, engineio_logger=True,cors_allowed_origins='*')
# 用一个WSGI应用程序封装
app = socketio.WSGIApp(sio)


# connect跟disconnect 是特殊的两个事件,连接或断开时会自动调用
@sio.event
def connect(sid, environ):
print('connect ', sid)
# 当客户端连接时将客户端放入指定房间
# 当然 你也可以从environ 中获取客户端传过来的room_id
sio.enter_room(sid,room='user_1')

@sio.event
def disconnect(sid):
print('disconnect ', sid)
# 当客户端离开时,我们就删除这个房间
rooms = sio.rooms(sid)
for room in rooms:
sio.leave_room(sid, room)

if __name__ == '__main__':
SERVER_ADDRESS = ('', 8000)
sock = eventlet.listen(SERVER_ADDRESS)
eventlet.wsgi.server(sock, app)

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import socketio

sio = socketio.Client()

@sio.event
def connect():
print('connection established')


# 接收外部用户发送的消息
@sio.event
def mgr_tst(data):
print('message received with ', data)


@sio.event
def disconnect():
print('disconnected from server')

sio.connect('http://127.0.0.1:8000')
sio.wait()

模拟外部发送消息

1
2
3
4
5
6
7
import socketio

mgr = socketio.RedisManager('redis://127.0.0.1:6379/0',write_only=True)

# 这里其实是发送给客户端
# 这里坑了我好久,一开始我以为是发送给服务端然后服务端再转发给客户端,其实不是,是直接通过房间发送给指定客户端
mgr.emit('mgr_tst',data='client Msg',room='user_1')