长轮询
在网页,我们经常扫码登录,结合之前的学习的知识点,来思考下,前端是如何知道用户在手机上扫码登录了呢?
长轮询:
客户端不断的向服务器发送请求
缺点:
1. 开销大 2. 浪费资源 3. 消耗流量
websocket介绍
长轮询消耗太多资源,其中主要原因是客户端和服务端并没有一直连接在一起,如果能够让客户端和服务器一直保持连接呢?
正经介绍
WebSocket 协议是基于 TCP 的一种新的 HTML5 网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范
简单说
客户端和服务器一直连接在一起
websocket 服务端编程
第一步:导入
from tornado.websocket import WebSocketHandler
第二步:基类
class BaseWebsocketHandler(WebSocketHandler,SessionMixin): def get_current_user(self): current_user = self.session.get('user_ID') if current_user: return current_user return None
第三步:跳转 Handler
class IndexHandler(BaseHandler): @authenticated def get(self): self.render('08websocket.html')
第四步: websocket的Handler
class MessageHandler(BaseWebsocketHandler): ''' 建立连接 收发数据 断开连接 ''' users = set() #当我们的服务器想主动发送消息给浏览器的时候,调用write_message def open(self,*args,**kwargs): '''建立连接完成的代码逻辑''' MessageHandler.users.add(self) #服务器要告诉每一个客户端有人上线 for i in self.users: # i.write_message('%s 上线了'%self.request.remote_ip) i.write_message('%s %s上线了'%(self.current_user,datetime.now())) def on_message(self, message): '''收发数据的代码逻辑''' print(message) for i in self.users: i.write_message('%s -%s 说:%s'%(self.current_user,datetime.now(),message)) def on_close(self): '''断开连接的代码逻辑''' MessageHandler.users.remove(self) #服务器要告诉每一个客户端有人下线 for i in self.users: # i.write_message('%s 下线了'%self.request.remote_ip) i.write_message('%s %s下线了'%(self.current_user,datetime.now()))
展示结果如下:
07-websocket.py代码如下:
import sys import time import tornado.web import tornado.ioloop import tornado.httpserver import tornado.options from tornado.web import RequestHandler,authenticated from tornado.options import define,options from pycket.session import SessionMixin from tornado.websocket import WebSocketHandler import util.ui_modules import util.ui_methods # import time from datetime import datetime from data.connect import session from data.user_modules import UserDetails, User define('port',default=8080,help='run server',type=int) class BaseHandler(RequestHandler,SessionMixin): '''声明websocket基类''' def get_current_user(self): current_user = self.session.get('user_ID') if current_user: return current_user return None class BaseWebsocketHandler(WebSocketHandler,SessionMixin): def get_current_user(self): current_user = self.session.get('user_ID') if current_user: return current_user return None class IndexHandler(BaseHandler): @authenticated def get(self): self.render('08websocket.html') class LoginHandler(BaseHandler): def get(self): next_name = self.get_argument('next','') self.render('in_out.html',nextname=next_name) def post(self): '''验证逻辑''' user = self.get_argument('name',None) password = self.get_argument('password',None) next_name = self.get_argument('next','') # print(next_name) # username = session.query(User).filter(User.username == user).first() username = User.get_name(user) # print(username) if username and password == username.password: #如果判断用户可以登录,我们设置这样一个加密的cookie进去 # self.set_secure_cookie('user_ID',user) self.session.set('user_ID',user) self.redirect(next_name) else: self.write('登录失败') class MessageHandler(BaseWebsocketHandler): ''' 建立连接 收发数据 断开连接 ''' users = set() #当我们的服务器想主动发送消息给浏览器的时候,调用write_message def open(self,*args,**kwargs): '''建立连接完成的代码逻辑''' MessageHandler.users.add(self) #服务器要告诉每一个客户端有人上线 for i in self.users: # i.write_message('%s 上线了'%self.request.remote_ip) i.write_message('%s %s上线了'%(self.current_user,datetime.now())) def on_message(self, message): '''收发数据的代码逻辑''' print(message) for i in self.users: i.write_message('%s -%s 说:%s'%(self.current_user,datetime.now(),message)) def on_close(self): '''断开连接的代码逻辑''' MessageHandler.users.remove(self) #服务器要告诉每一个客户端有人下线 for i in self.users: # i.write_message('%s 下线了'%self.request.remote_ip) i.write_message('%s %s下线了'%(self.current_user,datetime.now())) application = tornado.web.Application( handlers=[ (r'/index',IndexHandler), (r'/login',LoginHandler), (r'/websocket',MessageHandler), ], debug=True, template_path = 'templates', static_path='static', # autoescape = None, #全局取消转义 ui_methods=util.ui_methods, ui_modules=util.ui_modules, cookie_secret ='qwe123', #cookie加盐 login_url = '/login', pycket = { 'engine':'redis', 'storage':{ 'host':'localhost', 'port':6379, 'db_sessions':5, 'max_connections':2**10, }, 'cookies':{ 'expires_days':7 } } ) if __name__ == '__main__': tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(application) http_server.listen(options.port) tornado.ioloop.IOLoop.current().start()
webscoket 客户端编程
服务器已经解决,那么在客户端该怎么做呢?
WebSocket
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() :主动关闭现有链接
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:8080/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>