tornado - 原生websocket
WebSocket概念
传统的HTTP与HTML技术使用客户端主动向服务器发送请求并获得回复的应用场景。但随着即时通信需求的增多,这样的通信不能满足要求。
websocket与普通的socket通信类似,打破原来的request和response一对一的通信模型,同时打破了服务器被动接收客户端请求的应用场景。
websocket是基于HTTP的协议。是用http协议规定传递。协议规定了浏览器和服务端创建连接之后,不断开,保持连接。相互之间可以基于连接进行主动的收发消息,即时通信。
服务端编程
Tornado定义了WebSocketHandler类用于处理websocket连接的请求,可以继承该类实现其中的open()
、on_message()
、on_close()
函数。
open()
:在一个新的连接建立时,会调用此函数,可以获取客户端提交的参数,以及可以操作cookie等。on_message()
:建立连接后,当收到来自客户端的请求时,会调用此函数。可以通过解析接收到的信息做相应的处理。on_close()
:当连接被关闭时,会调用此函数。再此函数中,可以通过访问self.close_code
和self.close_reason
查询关闭的原因。
除了上述3个字段调用的函数外,还提供了2个开发者主动操作websocket的函数。
write_message(message,binary=Flase)
: 用于向与本链接相对于的客户端写消息。close(code=None,reaon=None)
: 主动关闭连接,其中code和reason用于告诉客户端连接被关闭的原因。code参数必须是一个数值,而reason是一个字符串。
import tornado.ioloop
import tornado.web
import tornado.websocket
from tornado.options import define, options, parse_command_line
define("port", default=8888, help="run on the given port", type=int)
clients = dict()
class IndexHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
self.render("index.html")
class MyWebSocketHandler(tornado.websocket.WebSocketHandler):
# 有新链接时被调用执行
def open(self, *args):
self.id = self.get_argument("id")
self.stream.set_nodelay(True)
client[self.id] = {"id": self.id, "object": self} # 保存session到字典中
# 收到消息时被调用
def on_message(self, message):
print("客户端%s发来一条消息: %s"%(self.id, message))
# 关闭链接时被调用
def on_close(self):
if self.id in clients:
del clients(self.id)
print("客户端%s断开连接"%(self.id))
def check_origin(self, origin):
return True
app = tornado.web.Application([
(r'/', IndexHandler),
(r'/websocket', MyWebSocketHandler)
])
import threading
import time
import datetime
# 启动单独的线程运行此函数,每隔一秒向所有的客户端推送当前时间
def send_time():
while True:
for key in clients.keys():
msg = str(datetime.datetime.now())
clients[key]["object"].write_message(msg) #主动发消息
print("发送给客户端%s:%s"%(key, msg))
time.sleep(1)
if __name__=='__main__':
threading.Thread(target=send_time).start() # 启动推送时间的线程
parse_command_line()
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
客户端编程
由于websocket时HTML5的标准之一,所有主流浏览器的web客户端编程语言JavaScript可以支持websocket的客户端编程。
JavaScript初始化websocket对象:
var socket = new WebSocket(url);
在代码中只需给websocket的构造函数传入URL地址,比如http://xxx.com。可以为该对象的如下事件指定处理函数以响应它们。
WebSocket.onopen
: WebSocket连接建立时调用。WebSocket.onmessage
: 接收到来自服务器的消息时调用。WebSocket.onerror
: 通信错误时调用。WebSocket.onclose
: 与服务器连接断开时调用。
还可以主动进行操作:
WebSocket.send(data)
: 向服务器发送消息。WebSocket.close
: 主动关闭连接。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket</title>
</head>
<body>
<a href="javascript:WebSocketTest()">Run WebSocket</a>
<div id="msg" style="height:200px; background:black; color:white;"></div>
</body>
<script type="text/javascript">
var messageContainer = document.getElementById("msg");
function WebSocketTest(){
if ("Websocket" in window){
messageContainer.innerHTML = "Websocket is supported by your Browser!";
var ws = new WebSocket("ws://localhost:8888/websocket?id=12");
ws.onopen = function(){
ws.send("Message to send");
};
ws.onmessage = function(evt){
var recv_msg = evt.data;
messageContainer.innerHTML = messageContainer.innerHTML + "<br/>Message is received:" + recv_msg;
};
ws.onclose = function(){
messageContainer.innerHTML = messageContainer.innerHTML + "<br/>Connection is closed...";
};
}eles{
messageContainer.innerHTML = "WebSocket Not supported by your Browser!";
}
}
</script>
</html>
上述代码解析:
- Run WebSocket连接用于让用户启动Websocket;另一个id=msg的div标签用于显示服务器端传来的消息。
- 使用JavaScript语句
if ("Websocket" in window){}
可以判断当前浏览器是否支持websocket对象。 - 如果支持websocket对象,则连接服务器ws://localhost:8888/websocket,并且传入参数 id。然后通过websocket的处理函数,进行消息的展示。onopen事件向服务器发送消息。