即时通讯(Instant Messaging)是一种基于互联网的即时交流消息的业务。
类型可分为两种:
在线推送
适用:web页面 和 App自己构建IM服务器
使用WebSocket 采用成熟的框架方案Socket.IO 对于App还可自己封装socket
使用第三方IM服务商提供的服务
离线推送
适用:App
对于iOS,使用APNs
对于andorid,使用FCM(国外)或第三方IM服务商提供的服务
提供第三方IM服务的服务商有:网易、融云…
需求场景:
服务器主动给客户端推送消息
用户下了订单,需要在运营管理后台向运营人员推送新订单通知
用户A关注了用户B,系统需要向用户B推送提示消息
即时聊天
HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket是一种在单个TCP连接上进行全双工通信的协议。在WebSocket API中,浏览器和服务器只需要完成一次握手(不是指建立TCP连接的那个三次握手,是指在建立TCP连接后传输一次握手数据),两者之间就直接可以创建持久性的连接,并进行双向数据传输。
Websocket使用ws或wss的统一资源标志符,类似于HTTPS,其中wss表示在TLS之上的Websocket。如
ws://example.com/wsapi
wss://secure.example.com/
握手协议
WebSocket 是独立的、创建在 TCP 上的协议。 报文,为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)
Socket.IO 本是一个面向实时 web 应用的 JavaScript 库,现在已成为拥有众多语言支持的Web即时通讯应用的框架。Socket.IO 主要使用WebSocket协议。但是如果需要的话,Socket.io可以回退到几种其它方法,例如Adobe Flash Sockets,JSONP拉取,或是传统的AJAX拉取,并且在同时提供完全相同的接口。尽管它可以被用作WebSocket的包装库,它还是提供了许多其它功能,比如广播至多个套接字,存储与不同客户有关的数据,和异步IO操作。Socket.IO 不等价于 WebSocket,WebSocket只是Socket.IO实现即时通讯的其中一种技术依赖,而且Socket.IO还在实现WebSocket协议时做了一些调整。
优点:
Socket.IO 会自动选择合适双向通信协议,仅仅需要程序员对套接字的概念有所了解。
有Python库的实现,可以在Python实现的Web应用中去实现IM后台服务。
缺点
Socket.io并不是一个基本的、独立的、能够回退到其它实时协议的WebSocket库,它实际上是一个依赖于其它实时传输协议的自定义实时传输协议的实现。该协议的协商部分使得支持标准WebSocket的客户端不能直接连接到Socket.io服务器,并且支持Socket.io的客户端也不能与非Socket.io框架的WebSocket或Comet服务器通信。因而,Socket.io要求客户端与服务器端均须使用该框架。
安装
pip install python-socketio
创建服务器
方式1
使用多进程多线程模式的WSGI服务器对接(如uWSGI、gunicorn)
import socketio
# create a Socket.IO servers
sio = socketio.Server()
# 打包成WSGI应用,可以使用WSGI服务器托管运行
app = socketio.WSGIApp(sio) # Flask Django
创建好app对象后,使用uWSGI、或gunicorn服务器运行此对象。
方式2
作为Flask、Django 应用中的一部分
from wsgi import app # a Flask, Django, etc. application
import socketio
# create a Socket.IO server
sio = socketio.Server()
app = socketio.WSGIApp(sio, app)
创建好app对象后,使用uWSGI、或gunicorn服务器运行此对象。
方式3
使用协程的方式运行 (推荐)
import eventlet
eventlet.monkey_patch()
import socketio
import eventlet.wsgi
sio = socketio.Server(async_mode='eventlet') # 指明在evenlet模式下
app = socketio.Middleware(sio)
eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
编写事件处理方法,可以接收指定的事件消息数据,并在处理方法中对消息数据进行处理。
@sio.on('connect')
def on_connect(sid, environ):
"""
与客户端建立好连接后被执行
:param sid: string sid是socketio为当前连接客户端生成的识别id
:param environ: dict 在连接握手时客户端发送的握手数据(HTTP报文解析之后的字典)
"""
pass
@sio.on('disconnect')
def on_disconnect(sid):
"""
与客户端断开连接后被执行
:param sid: string sid是断开连接的客户端id
"""
pass
# 以字符串的形式表示一个自定义事件,事件的定义由前后端约定
@sio.on('my custom event')
def my_custom_event(sid, data):
"""
自定义事件消息的处理方法
:param sid: string sid是发送此事件消息的客户端id
:param data: data是客户端发送的消息数据
"""
pass
注意
connect 为特殊事件,当客户端连接后自动执行
disconnect 为特殊事件,当客户端断开连接后自动执行
connect、disconnect与自定义事件处理方法的函数传入参数不同
发送消息
群发
sio.emit('my event', {'data': 'foobar'})
给指定用户发送
sio.emit('my event', {'data': 'foobar'}, room=user_sid)
给一组用户发送
SocketIO提供了房间(room)来为客户端分组
sio.enter_room(sid, room_name)
将连接的客户端添加到一个room
@sio.on('chat')
def begin_chat(sid):
sio.enter_room(sid, 'chat_users')
注意:当客户端连接后,socketio会自动将客户端添加到以此客户端sid为名的room中
sio.leave_room(sid, room_name)
将客户端从room移除
@sio.on('exit_chat')
def exit_chat(sid):
sio.leave_room(sid, 'chat_users')
查询sid客户端所在的所有房间
sio.rooms(sid)
给一组发送消息
@sio.on('my message')
def message(sid, data):
sio.emit('my reply', data, room='chat_users')
也可在群组发消息时跳过指定客户端
@sio.on('my message')
def message(sid, data):
sio.emit('my reply', data, room='chat_users', skip_sid=sid)
使用send发送message事件消息
对于’message’事件,可以使用send方法
sio.send({'data': 'foobar'})
sio.send({'data': 'foobar'}, room=user_sid)
import socketio
sio = socketio.Client()
@sio.on('connect')
def on_connect():
pass
@sio.on('event')
def on_event(data):
pass
sio.connect('http://10.211.55.7:8000')
sio.wait()
测试可以使用谷歌的插件:firecamp