tornado websocket
一、长轮询
在扫码登录的页面,服务器是如何知道用户有没有在手机上扫码登录?
通过长轮询的方式,每隔几秒向服务器发送一个请求,服务器判断这个请求中有没有用户扫码。
缺点
1.开销大
2.浪费资源
3.消耗流量
二、websocket概念
长轮询消耗太多资源,其中主要原因是客户端和服务端并没有一直连接在一起。
websocket起到的作用,就是让客户端和服务器一直连接在一起。
(一).websocket官话描述
WebSocket协议是基于TCP的一种新的HTML5网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。
(二).简单理解
客户端和服务器一直连接在一起
三、websocket服务端编程
(一).导包
import tornado.websocket
(二).编写一个基类
class BaseWebSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin): def get_current_user(self): current_user = self.session.get('user') if current_user: return current_user return None
(三).跳转的Handler
class IndexHandler(BaseHandler): @authenticated def get(self): self.render('08websocket.html')
(四).websocket的Handler
class MessageWSHandler(BaseWebSocketHandler): users = set() def open(self): MessageWSHandler.users.add(self) def on_message(self, message): for u in self.users: u.write_message( '%s-%s-说:%s' % ( self.current_user, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), message )) def on_close(self): if self in MessageWSHandler.users: MessageWSHandler.users.remove(self)
(五).为WebsocketHandler添加路由
(六).总结
Tornado 定义了 tornado.websocket.WebSocketHandler 这个类,用于处理 WebSocket 链接的请求。
必须继承该类并重写 open()、on_message()、on_close() 这三个函数,Tornado会自动去调用这三个函数。
(1).WebSocketHandler还提供了两个可以主动操作的WebSocket函数
WebSocketHandler.write_message(message)函数:用于向与本链接相对应的客户端写消息。
WebSocketHandler.close(code=None,reason=None)函数:主动关闭 WebSocket链接。其中的code和reason用于告诉客户端链接被关闭的原因。参数code必须是一个数值,而reason是一个字符串。
四、webscoket客户端编程
WebSocket是HTML5的标准之一,主流浏览器的web客户端编程语言Javascript已经支持WebSocket的客户端编程。
(一).初始化WebSocket对象
var socket = new WebSocket(url);
(二).处理函数
WebSocket.onopen:此事件发生在 WebSocket 链接建立时
WebSocket.onmessage:此事件发生在收到了来自服务器的消息时
WebSocket.onclose:此事件发生在与服务器的链接关闭时
WebSocket.onerror:此事件发生在通信过程中有任何错误时
(三).主动操作函数
WebSocket.send(data):向服务器发送消息
WebSocket.close():主动关闭现有链接
五、完整代码
(一).编写Handler
class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): current_user = self.get_secure_cookie('ID') if current_user: return current_user return None class BaseWebsocketHandler(tornado.websocket.WebSocketHandler): def get_current_user(self): current_user = self.get_secure_cookie("ID") if current_user: return current_user return None class MessageWSHandler(BaseWebsocketHandler): users = set() def show_message(self, message): for u in self.users: u.write_message(message) def on_open(self): print("coming open...") self.show_message("{}进入!".format("")) MessageWSHandler.users.add(self) # self是user的实例 def on_message(self, message): print(message) for u in self.users: self.show_message("{}说:{}".format("", message)) # self.write_message(message) def on_close(self): if self in MessageWSHandler.users: self.show_message("{}退出!".format("")) MessageWSHandler.users.remove(self) class MainHandler(BaseWebsocketHandler): @authenticated def get(self): self.render("08websocket.html")
(二).编写模板文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> WebSocket </title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <style> * { margin: 0; padding: 0; } .box { width: 800px; margin-left: auto; margin-right: auto; margin-top: 25px; } #text { width: 685px; height: 130px; border: 1px solid skyblue; border-radius: 10px; font-size: 20px; text-indent: 1em; resize: none; outline: none; } #text::placeholder { color: skyblue; } .btn { width: 100px; margin: -27px 0 0px 8px; } #messages { padding-left: 10px; font-size: 25px; } #messages li { list-style: none; color: #000; line-height: 30px; font-size: 18px; } </style> </head> <body> <div class="box"> <div> <textarea id="text" placeholder="请输入您的内容"></textarea> <a href="javascript:WebSocketSend();" class="btn btn-primary">发送</a> </div> <ul id="messages"> </ul> </div> <script src="{{ static_url('js/jquery-2.2.0.min.js') }}"></script> <script type="text/javascript"> var mes = document.getElementById('messages'); if ("WebSocket" in window) { mes.innerHTML = "发送WebSocket请求成功!"; // var ws = new WebSocket("ws://127.0.0.1:8000/websocket"); var ws = new WebSocket("ws://47.98.139.237:8888/websocket"); ws.onopen = function () { alert('连接已打开请聊天') }; ws.onmessage = function (goudan) { var received_msg = goudan.data; var aLi = $("<li>" + received_msg + "</li>"); // $(mes).append($(aLi)) // 方法一 // $(aLi).appendTo(mes); // 方法二 $(mes).prepend($(aLi)) }; ws.onclose = function () { mes.innerHTML = mes.innerHTML + "<br>连接已经关闭..."; }; } else { mes.innerHTML = "发送WebSocket请求失败!" } function WebSocketSend() { ws.send($("#text").val()); $("#text").val("") } </script> </body> </html>
前端脚本是jquery写法,必须引入jquery.js
六、可能会踩到的坑
(一).代理插件引起的websocket失败
(1).问题描述
我在Chrome上安装了一个代理插件,进行科学上网。但使用websocket的时候,会报错。把报错的英文进行翻译:通过代理服务器建立隧道失败。
没有代理插件的浏览器就可以成功发送消息,而且在Chrome上也可以接收到已发送过的数据,但是发送数据就会失败。
(2).原因
代理是把请求通过代理服务器转发给真正的服务器。一般代理只做了http和https,基本不会去做websocket,哪怕是专业的代理。
(3).解决办法
1.退出代理账号,然后重新访问,一切都正常了。
2.换个没有代理插件的浏览器。
3.加白名单,不让插件去代理websocket
(4).补充
尚不清楚蓝灯或者其他科学上网方式会不会遇到类似问题。如果有类似问题,一定要直接评论哈~让别人不要踩坑了