概念
概念 :在HTTP客户端与服务端之间建立持久连接的HTML5的标准技术。
实现 :浏览器与服务器的全双工通信(full-duplex)。
适用场景 : 服务器主动向客户端发送消息
传统实现方式 :Ajax 、Long poll 等动态客户端技术 均采用轮询技术实现,耗费网络带宽,计算资源
WebSocket 通信原理
浏览器端请求
# 客户端发送HTTP Request 告诉服务器建立一个 websocket长链接通道
# 请求头 header中在原基础上增加如下
Connection:Upgrade
sec-WebSocket-Key:uRovscZjNol/umbT5uKmw== # 密钥为 uRovscZjNol/umbT5uKmw==
Upgrade:websocket
sec-WwbSocket-Version:13 # 版本号 13
服务器端回复
Connection:Upgrade
Upgrade:websocket
# 对客户端端发送过来的密钥进行加密返回,以便客户端确认服务器正常
sec-WebSocket-Accept:rLHCkw/SKs09GAH/ZSFhBATDKrU==
服务器端基本方法
"""
tornado 定义了tornado.socket.WebSocketHandler类用于处理WebSocket链接请求
开发者应该集成该类,并实现其中的open()、on_message()、on_close() 函数
"""
WebSocketHandler.open() # 在一个新的WebSocket 建立连接时,框架调用此函数
WebSocketHandler.on_message() # 建立长连接后,当收到客户端的请求时,框架调用此函数,解析收到消息,并作出应答
WebSocketHandler.on_close() # 关闭连接时,执行此函数,可以可以通过self.close_code\self.close_reason 查询关闭原因
WebSocketHandler.write_message(message,binary = False ) # 向连接的客户端写消息
# code、reason 用于告诉客户端被关闭的原因,参数code为一个数值,而 reason 为一个字符串
WebSocketHandler.close(code = None,reason = None) # 主动关闭WebSocket链接、
服务器端编程
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):
# index 中包含 webscket 客户端程序
self.render("index.html")
# 处理 websocket 请求
class MyWebSocketHandler(tornado.socket.WebSocketHandle):
# 当有新链接时调用
def open(self,*args):
# 获取链接用户提交的Id
self.id = self.get_argument("Id")
self.stream.set_nodelay(True)
# 保存 session 到 clients 字典中
clients[self.id] = {"id":self.id,"object":self}
# 接收到消息时被调用
def on_message(self,message):
print("Client %s received a message: %s"%(self.id,message))
# 关闭链接时被调用
def on_close(self):
if self.id in clients:
# 将 websocket 从 clients 中删除
del clients[self.id]
print("Client %s is closed"%(self.id))
def check_origin(slef,origin):
return True
app = tornado.web.Application([
(r'/',IndexHandler),
(r'/websocket',MyWebSocketHandler),
])
import threading
import time
# 单线程运行函数,每秒向客户端发送推送当前时间
def sendTime():
import datetime
while True:
for key in clients.keys():
msg = str(datetime.datetime.now())
clients[key]["object"].write_message(msg) # 发送信息
print("write to client %s:%s"%(key.msg))
time.sleep(1)
if __name__ =='__main__':
# 两个线程、一个发送时间、一个接口监听
threading.Thread(target= sendTime).start() # 启动推送线程
parse_command_line()
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start() # 挂起运行
客户端编程
浏览器基本方法
"""
主流的浏览器的Web客户端编程语言 JavaScript 已经支持 WebSocket的客户端编程
客户端 JavaScript 可以通过如下代码初始化 WebSocket 对象
"""
var Socket = new WebSocket(url) # 传入服务器的URL地址
WebSocket.onopen # 此事件发生在WebSocket 链接建立时
WebSocket.onmessage # 此事件发生在收到来自服务器的消息时
WebSocket.onreeor # 此事件发生在通信过程中有任何错误时
WebSocket.onclose # 此事件发生在与服务器的链接关闭时
WebSocket.send(data) # 向服务器发送消息
WebSocket.close() # 主动关闭现有链接