WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
Tornado中定义了tornado.websocket.WebSocketHandler来处理websocket请求。
Tornado框架的安装以及入门这里就不说了,想入门tornado的请移步我的另一篇文章奥小飞:基于Python的Tornado框架入门zhuanlan.zhihu.com
WebSocketHandler类中方法简介
open()
当websocket连接建立后被调用
on_message(message) *该方法必须被重写
当收到客户端发送的消息时被调用
on_close()
当websocket连接关闭后被调用
write_message(message, binary=False)
向客户端发送消息,message可以是字符串或字典(字典会被转为json)。若binary为False,则message以utf8编码发送;二进制模式(binary=True)时,可发送任何字节码。
close()
关闭websocket连接
check_origin(origin)
判断源origin,对于符合条件(返回判断结果为True)的请求源origin允许其连接,否则返回403。可以重写此方法来解决WebSocket的跨域请求(如始终return True)。
websocket demo
import tornado.ioloop
import tornado.web
import tornado.websocket
class ConnectHandler(tornado.websocket.WebSocketHandler) :
def check_origin(self, origin) :
'''重写同源检查 解决跨域问题'''
return True
def open(self) :
'''新的websocket连接后被调动'''
self.write_message('Welcome')
def on_close(self) :
'''websocket连接关闭后被调用'''
def on_message(self, message) :
'''接收到客户端消息时被调用'''
self.write_message('new message :' + message) # 向客服端发送
class MainHandler(tornado.web.RequestHandler) :
def get(self) :
self.write("Hello world")
class Application(tornado.web.Application) :
def __init__(self) :
handlers = [
(r'/index', MainHandler),
(r'/ws', ConnectHandler)
]
tornado.web.Application.__init__(self, handlers)
if __name__ == "__main__" :
app = Application()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
前端页面demo
Tornado WebSocketConnect websocket:
function sendMsg() {
if ("WebSocket" in window) {
var ws = new WebSocket("ws://localhost:8000/ws");
ws.onopen = function() {
ws.send("Hello");
};
ws.onmessage = function (evt) {
alert(evt.data);
};
}
}
这个demo创建了一个websocket服务并监听8000端口,在浏览器建立连接之后会触发open()方法向浏览器推送“welcome”消息,在收到浏览器发送的消息后会触发on_message()方法向浏览器推送“new message :”消息。
到此为之,一个简单的websocket应用已经可以了。
但是这个demo太过于简单,他只实现了在建立连接时和收到消息时的一些操作,并没有体现websocket的主要作用(实时推送数据)。
websocket demo 进阶
import tornado.ioloop
import tornado.web
import tornado.websocket
class ProStatus():
connector = {} # 记录当前连接的user
def user_connect(self, user):
if user not in self.connector:
self.connector[user] = set()
def user_remove(self, user):
self.connector.remove(user)
def trigger(self, message):
''' 向所有被记录的客户端推送最新内容 '''
for user in self.connector:
user.write_message(message)
class ReceiveHandler(tornado.web.RequestHandler):
def get(self):
msg = self.get_argument('msg', '')
ProStatus().trigger(msg) # 接收到消息之后推送
class ConnectHandler(tornado.websocket.WebSocketHandler):
def check_origin(self):
'''重写同源检查 解决跨域问题'''
return True
def open(self):
'''新的websocket连接后被调动'''
ProStatus().user_connect(self) #用户连接后记录
self.write_message('Welcome')
def on_close(self):
'''websocket连接关闭后被调用'''
ProStatus().user_remove(self) # 断开连接后remove
def on_message(self, message):
'''接收到客户端消息时被调用'''
self.write_message('new message :' + message)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello world")
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r'/index', IndexHandler),
(r'/ws', ConnectHandler),
(r'/receive', ReceiveHandler)
]
tornado.web.Application.__init__(self, handlers)
if __name__ == "__main__":
app = Application()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
问题
心跳包
如果websocket隔一段时间不推送数据,那么前端的连接就会自动断开 所以前端建立websocket连接时,需要加入心跳包机制。 当然心跳包不单单是为了解决该问题,也是为了解决各种意外断开的情况,心跳包很有必要。 心跳包的原理就是每隔一定的时间检查websocket连接是否断开,在没有断开的情况下隔一定的时间给服务端发送一条消息,这条消息要确保服务端可以回应,如果断开则进行重连。至于前端具体怎么实现心跳包的代码请自行百度。