Python的tornado框架中,websocket允许浏览器和服务器之间进行双向,实时,持久的,全双工通信。协议本身使用 ws://URL
的格式,如果是加密的websocket则使用的是wss://URL
websocket的起源
websocket和其他方式的比较:
1、http协议中的”keep-alive connection”,是指一次tcp连接中完成多个http请求,但对每个请求依然要单独发header;
2、轮询(Polling),是指从客户端(一般就是浏览器)不断主动的向服务器http请求查询是否有新数据;
3、Comet技术,Comet技术又细分为两种实现方式:一种是长轮询机制,一种是流技术;
这几种方式具有很明显的缺点,就是需要由浏览器对服务器发出http request,大量消耗服务器的带宽和资源(就是除了真正的数据部分外,服务器和客户端还要大量交换http header,信息交换率很低),为了解决这种状况,Websocket能够更好的节省服务器资源和带宽并实现真正意义上的实时推送;
websocket的优点:
1、websocket是独立的,建立在tcp上的协议,和http协议唯一的关联就是使用http协议的101状态码进行协议切换,使用的tcp端口是80,可以用于绕过大多数防火墙的限制;
2、websocket使得客户端和服务器之间的数据交换变得更加简单,允许服务器端直接向客户端推送数据而不需要客户端进行请求;
3、websocket的客户端和服务器之间可以创建持久性的连接,并允许数据进行双向传送;
websocket协议与http协议的联系:
websocket和http协议一样,都是基于tcp的,所以websocket也是一种可靠的协议,Web开发者调用的websocket的send()函数在browser(浏览器)的实现最终都是通过tcp的系统接口进行传输的。
websocket和http协议一样都是应用层的协议,websocket在建立握手连接时,数据是通过http协议传输的,但是在建立连接之后,真正的数据传输阶段是不需要http协议参与的。
下图是协议栈中的websocket和http的关系:
tornado的websocket模块:
1、在使用时应该添加 from tornado.websocket import WebSocketHandler
,或者要导入tornado的以下几个包
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
WebSocketHandler类,这个类提供了和已连接的客户端进行通信的WebSocket通信的事件和钩子;
2、WebSocketHandler.open()
当一个WebSocket连接建立后被调用;
3、WebSocketHandler.on_message(message)
当客户端发送消息message过来时被调用,此方法必须重写;
4、WebSocketHandler.on_close()
当WebSocket连接关闭后被调用;
5、WebSocketHandler.write_message(message,binary=False)
向客户端发送消息message,message可以是字符串或字典(字典会被转为json字符串);binary是False,则message以utf8编码的形式发送;binary=True,message可以发送任何字节码;
6、WebSocketHandler.close()
关闭websocket连接
7、WebSocketHandler.check_origin(origin)
重写此方法可以解决websocket的跨域请求;判断源origin,对于符合条件(返回结果为True)的请求源origin允许其连接,否则返回403;
//支持跨域请求
def check_origin(self, origin):
return True
//允许所有子域下的连接
def check_origin(self, origin):
parsed_origin = urllib.parse.urlparse(origin)
return parsed_origin.netloc.endswith(".mydomain.com")
websocket客户端的常见API:
1、WebSocket构造函数,用于新建一个WebSocket实例,当执行下列语句之后,客户端就会与服务器进行连接;
var ws = new WebSocket('ws://localhost:8080/ws');
2、WebSocket.readyState,readyState属性返回实例对象的当前状态,总共有以下四种状态:
CONNECTING:值为0,表示正在连接;
OPEN:值为1,表示连接成功,可以正常通信;
CLOSING:值为2,表示连接正在关闭;
CLOSED:值为3,表示连接已经关闭,或者打开连接失败;
3、WebSocket.onopen,实例对象的open属性,用来指定连接成功后的回调函数;
//ws.send(message),客户端向服务器发送消息
ws.onopen = function (){
ws.send('Hello Server!');
}
如果用户想要指定多个回调函数,可以使用addEventListener
方法;
ws.addEventListener('open',function(event){
ws.send('Hello Server!');
});
4、WebSocket.onclose,实例对象的onclose属性,用于指定连接关闭后的回调函数;
ws.onclose = function() {
};
5、WebSocket.onmessage,实例对象的onmessage属性,用于指定收到服务器数据之后的回调函数;
ws.onmessage = function(event){
var data = event.data;
//处理数据
};
ws.addEventListener("message",function(event){
var data = event.data;
//处理数据
});
6、WebSocket.bufferedAmount,实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去,它可以用来判断是否发送结束;
var data = new ArrayBuffer(10000000);
ws.send(data);
if(ws.bufferedAmount == 0){
//发送结束
}
else{
//发送未结束
}
7、WebSocket.onerror,实例对象的onerror属性,用于指定报错时候的回调函数;
ws.onerror = function(event){
//处理错误事件
}
ws.addEventListener("error",function(event){
//处理错误事件
});
一个简单的关于websocket的例子:
1、程序框架
2、code
#如何开启服务--> python2.7 ws_test.py
index.html
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>tornado websocket testtitle>
head>
<script type="text/javascript">
var ws;
function onLoad(){
ws = new WebSocket("ws://100.82.23.37:9999/websocket");
ws.onmessage = function(e){
alert(e.data)
}
}
function SendMessage(){
ws.send(document.getElementById('msg').value);
}
script>
<body onload='onLoad();'>
要发送的消息:<input type="text" id="msg" />
<input type="button" onclick="SendMessage();" value="提交" />
body>
html>
ws_test.py
#!/usr/local/bin/python2.7
#-*- encoding:utf-8 -*-
import tornado.web
import tornado.websocket
import tornado.httpserver
import tornado.ioloop
class MyHandlerIndex(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
class MyWebSocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self,origin):
return True
def open(self):
pass
def on_message(self,message):
self.write_message(u"Your message is :" + message)
def on_close(self):
pass
class MyHandlerApplication(tornado.web.Application):
def __init__(self):
handlers = [
(r'/', MyHandlerIndex),
(r'/websocket',MyWebSocketHandler)
]
setting = {'template_path' : 'templates'} tornado.web.Application.__init__(self,handlers,**setting)
if __name__ == '__main__':
ws_app = MyHandlerApplication()
server = tornado.httpserver.HTTPServer(ws_app)
server.listen(9999)
tornado.ioloop.IOLoop.instance().start()
3、执行结果
参考资料:
tornado-websocket-浏览器与服务器双向通信
websocket学习简书
http://www.ruanyifeng.com/blog/2017/05/websocket.html
http://www.giantflyingsaucer.com/blog/?p=4586