HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,而且可以更实时地进行通信。html
在2008年诞生,2011年成为国际标准。python
如今基本全部浏览器都已经支持了。程序员
WebSocket是一种在单个TCP链接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只须要完成一次握手(不是指创建TCP链接的那个三次握手,是指在创建TCP链接后传输一次握手数据),二者之间就直接能够建立持久性的链接,并进行双向数据传输。web
Websocket使用ws或wss的统一资源标志符,相似于HTTPS,其中wss表示在TLS之上的Websocket。如:后端
ws://example.com/wsapi wss://secure.example.com/
Websocket使用和 HTTP 相同的 TCP 端口,能够绕过大多数防火墙的限制。默认状况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。api
WebSocket 是独立的、建立在 TCP 上的协议。 报文浏览器
Websocket 经过 HTTP/1.1 协议的101状态码进行握手。服务器
为了建立Websocket链接,须要经过浏览器发出请求,以后服务器进行回应,这个过程一般称为“握手”(handshaking)。websocket
一个典型的Websocket握手请求以下:多线程
客户端请求
GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: example.com Origin: http://example.com Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== Sec-WebSocket-Version: 13
服务器回应
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s= Sec-WebSocket-Location: ws://example.com/
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)
由于服务器与客户端进行即时通讯时,会尽量的使用长链接,因此若服务器采用多进程或多线程方式运行,受限于服务器能建立的进程或线程数,可以支持的并发链接客户端不会很高,也就是服务器性能有限。采用协程方式运行服务器,能够提高即时通讯服务器的性能。
不一样于HTTP服务的编写方式,SocketIO服务编写再也不以请求Request和响应Response来处理,而是对收发的数据以消息(message)来对待,收发的不一样类别的消息数据又以事件(event)来区分。
本来HTTP服务编写中处理请求、构造响应的视图处理函数在SocketIO服务中改成编写收发不一样事件的事件处理函数。
编写事件处理方法,能够接收指定的事件消息数据,并在处理方法中对消息数据进行处理。
@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
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')
sio.rooms(sid)
查询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()