Tornado 之 WebSocket
7.3 WebSocket
WebSocket是HTML5规范中新提出的客户端-服务器通讯协议,协议本身使用新的ws://URL格式。
WebSocket 是独立的、创建在 TCP 上的协议,和 HTTP 的唯一关联是使用 HTTP 协议的101状态码进行协议切换,使用的 TCP 端口是80,可以用于绕过大多数防火墙的限制。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,两者之间可以创建持久性的连接,并允许数据进行双向传送。
目前常见的浏览器如 Chrome、IE、Firefox、Safari、Opera 等都支持 WebSocket,同时需要服务端程序支持 WebSocket。
1. Tornado的WebSocket模块
Tornado提供支持WebSocket的模块是tornado.websocket,其中提供了一个WebSocketHandler类用来处理通讯。
WebSocketHandler.open()
当一个WebSocket连接建立后被调用。
WebSocketHandler.on_message(message)
当客户端发送消息message过来时被调用,注意此方法必须被重写。
WebSocketHandler.on_close()
当WebSocket连接关闭后被调用。
WebSocketHandler.write_message(message, binary=False)
向客户端发送消息messagea,message可以是字符串或字典(字典会被转为json字符串)。若binary为False,则message以utf8编码发送;二进制模式(binary=True)时,可发送任何字节码。
WebSocketHandler.close()
关闭WebSocket连接。
WebSocketHandler.check_origin(origin)
判断源origin,对于符合条件(返回判断结果为True)的请求源origin允许其连接,否则返回403。可以重写此方法来解决WebSocket的跨域请求(如始终return True)。
2. 前端JavaScript编写
在前端JS中使用WebSocket与服务器通讯的常用方法如下:
- var ws = new WebSocket("ws://127.0.0.1:8888/websocket"); // 新建一个ws连接
- ws.onopen = function() { // 连接建立好后的回调
- ws.send("Hello, world"); // 向建立的连接发送消息
- };
- ws.onmessage = function (evt) { // 收到服务器发送的消息后执行的回调
- alert(evt.data); // 接收的消息内容在事件参数evt的data属性中
- };
3. 在线聊天室的小Demo
后端代码 server.py
- import tornado.web
- import tornado.ioloop
- import tornado.httpserver
- import tornado.options
- import os,json
- import datetime
- from tornado.web import RequestHandler
- from tornado.options import define, options
- from tornado.websocket import WebSocketHandler
- class IndexHandler(RequestHandler):
- def get_current_user(self):
- '''
- 重写RequestHandler类中的get_current_user方法,用来判断当前是否是登录状态,请求中所有被@tornado.web.authenticated 装饰的方法,都需要此方法返回值不为None,否则给与403拒绝
- :return: 用户名或者None . 为None判断为非法请求,POST 时Tornado进行403禁止访问 ;GET 时 302 重定向到/login
- '''
- user = self.get_argument(name='username',default='None')
- if user and user != 'None':
- print('IndexHandler类 get_current_user获取到用户:',user)
- return user
- @tornado.web.authenticated #确认请求合法 依赖于get_current_user(self):函数的返回值作为判断请求是否合法
- def get(self):
- print("IndexHandler 收到GET请求")
- self.render("online_index.html",current_user=self.current_user)
- @tornado.web.authenticated
- def post(self, *args, **kwargs):
- print('IndexHandler 收到POST请求')
- self.render("online_index.html", current_user=self.current_user)
- class LoginHandler(RequestHandler):
- def get(self, *args, **kwargs):
- '''
- 处理输入昵称界面get请求
- :param args:
- :param kwargs:
- :return:
- '''
- cookie_value = self.get_secure_cookie('count')
- print('cookie_value :', cookie_value)
- count = int(cookie_value) + 1 if cookie_value else 1
- self.set_secure_cookie("count", str(count)) # 设置一个带签名和时间戳的cookie,防止cookie被伪造。
- #使用ajax方法做的前端
- # self.render('login_use_ajax.html')
- #使用form表单提交数据 的前端
- self.render('login_use_form.html')
- def post(self, *args, **kwargs):
- '''
- 暂时用不到
- :param args:
- :param kwargs:
- :return:
- '''
- pass
- # 继承tornado.websocket.WebSocketHandler,只处理WS协议的请求
- class ChatHandler(WebSocketHandler):
- def get_current_user(self):
- user = self.get_argument(name='username',default='None')
- if user and user != 'None':
- return user
- users = set() # 用来存放在线用户的容器
- @tornado.web.authenticated
- def open(self):
- print('收到新的WebSocket连接')
- self.users.add(self) # 建立连接后添加用户到容器中
- for u in self.users: # 向已在线用户发送消息
- u .write_message(u"[%s]-[%s]-%s 进入聊天室" % (
- self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self.current_user))
- def on_message(self, message):
- message = json.loads(message)
- print(type(message),message)
- for u in self.users: # 向在线用户广播消息
- u.write_message(u"[%s]-[%s]-说:<br>     %s" % ( datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self.current_user,message.get('msg')))
- def on_close(self):
- self.users.remove(self) # 用户关闭连接后从容器中移除用户
- for u in self.users:
- u.write_message(u"[%s]-[%s]-%s 离开聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),self.current_user))
- def check_origin(self, origin):
- return True # 允许WebSocket的跨域请求
- if __name__ == '__main__':
- tornado.options.parse_command_line() #允许命令行启动程序
- app = tornado.web.Application([ #定义处理web请求的应用程序
- (r"/", IndexHandler),
- (r"/login", LoginHandler),
- (r"/chat", ChatHandler), # 处理WebSocket协议传输的数据
- ],
websocket_ping_interval = 5, # WebSocket ping探活包发送间隔秒数- static_path = os.path.join(os.path.dirname(__file__), "statics"), #配置应用程序前端所需静态文件目录
- template_path = os.path.join(os.path.dirname(__file__), "templates"), #配置html文件路径
- login_url='/login', #配置登录url
- xsrf_cookies=True, #,防止跨站请求攻击,在post请求中起效,
- cookie_secret="2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A=", # 安全cookie用预置秘钥 base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
- debug = True #配置调试级别
- )
- http_server = tornado.httpserver.HTTPServer(app) #将应用处理逻辑 传递给HTTPServer 服务
- define("port", default=8000, type=int) #设置一个监听地址
- http_server.listen(options.port) #配置监听地址到 HTTPServe
- tornado.ioloop.IOLoop.current().start() #启动应用
前端代码online_index.html
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>聊天室</title>
- </head>
- <body>
- <div id="contents" style="height:500px;overflow:auto; border:green solid 2px"></div>
- <div>
- <textarea id="msg" style="width: 260px;height: 120px" placeholder="输入要发送的内容"></textarea>
- <input id="send_msg" type="button" href="javascript:;" onclick="sendMsg()" value="发送">
- </div>
- <script src="{{static_url('js/jquery.min.js')}}"></script>
- <script type="text/javascript">
- //创建ws协议连接,创建出来后就已经和服务端建立连接
- var ws = new WebSocket("ws://127.0.0.1:8000/chat?username={{current_user}}");
- //收到ws消息的时候在div对话框里面增加一行文字
- ws.onmessage = function(recv) {
- $("#contents").append("<p>" + recv.data + "</p>");
- };
- var username = "{{current_user}}";
- /////////////////////////监控按下回车键动作//////////////////////////
- // $("#send_msg").keypress(
- // function () {
- // console.log('发送消息');
- // sendMsg();
- // }
- // )
- ///////////////////////////////////////////////////////////////////////
- function sendMsg() {
- $('#msg').val($('#msg').val().trim());//去除输入前后的空格
- var msg = $("#msg").val();
- if ( msg && msg.length >0) {
- data = {
- // username:username,
- msg:msg
- };
- ws.send(JSON.stringify(data));
- $("#msg").val("");
- }else {
- alert('内容不许为空')
- }
- }
- </script>
- </body>
- </html>
前端login_use_form登录界面代码
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>登录聊天室</title>
- <script src="{{static_url('js/jquery.min.js')}}"></script>
- </head>
- <body>
- <!--使用form表单获取登录页-->
- <form action="/" method="post" onsubmit="return check_user()">
- {% module xsrf_form_html() %}
- <input id="username" name="username" placeholder="配置昵称,即可聊天">
- <input id="conform_username" type="submit" value="确认昵称" >
- </form>
- <script>
- function check_user(){
- $('#username').val($('#username').val().trim());//去除输入前后的空格
- var username = $('#username').val();
- if ( !username ){
- alert('昵称不能为空');
- return false;
- }else {
- alert('昵称OK');
- return true
- }
- }
- </script>
- </body>
- </html>
Tornado 之 WebSocket的更多相关文章
- tornado之WebSocket
WebSocket WebSocket是HTML5规范中新提出的客户端-服务器通讯协议,协议本身使用新的ws://URL格式. WebSocket 是独立的.创建在 TCP 上的协议,和 HTTP 的 ...
- tornado+websocket+mongodb实现在线视屏文字聊天
最近学了tornado和mongo,所以结合websocket 实现一个聊天功能,从而加深一下相关知识点的印象 1.websocket概览 webscoket是一种全双工通信模式的协议,客户端连接服务 ...
- Tornado长轮询和WebSocket
Http协议是一种请求响应式协议, 不允许服务端主动向客户端发送信息. 短轮询是一种简单的实现服务端推送消息的解决方案, 客户端以一定间隔自动向服务端发送刷新请求, 服务端返回要推送的消息作为响应. ...
- tornado WebSocket详解
1.什么是WebSocketwebsocket和长轮询的区别是客户端和服务器之间是持久连接的双向通信.协议使用ws://URL格式,但它在是在标准HTTP上实现的. 2.tornado的WebSock ...
- 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务
http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...
- websocket介绍
websocket应用 手动实现的websocket 你所见过的websocket 你一定见过在网站中,有一个游客聊天的聊天框,比如人人影视.这个聊天框是如何实现即时通讯的呢,就是用到了websock ...
- 你真的会websocket吗
Websocket WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端. WebSocket通信协议于2 ...
- 异步与websocket
异步与WebSockets 知识点 理解同步与异步执行过程 理解异步代码的回调写法与yield写法 Tornado异步 异步Web客户端AsyncHTTPClient tornado.web.asyn ...
- websocket基本概念
(1)websocket 协议 与 http协议 websocket 协议,双工通道 socket连接不断开 http 协议 ,只能请求响应 (用户主动获取) socket连接断开 都是基于socke ...
随机推荐
- python网络自动化运维之telnetlib实验(EVE-NG+pycharm)
运行环境: 物理机:win10 1903 网络设备:EVE-NG模拟器上运行思科三层路由器 网络设备OS版本:cisco ios(versions 15.6) python环境:pycharm 3.3 ...
- Codeforces 1466G - Song of the Sirens(哈希)
Codeforces 题面传送门 & 洛谷题面传送门 事实证明,有的难度评分不算很高.涉及的知识点不算很难的题目也能出得非常神仙 首先考虑如何暴力求答案.注意到一个文本串 \(T\) 在 \( ...
- Python如何支持读入gz压缩或未压缩文件?
目录 需求 示例代码 笨办法 Pythonic方法 需求 要写一个接口,同时支持压缩和未压缩文件读入 示例代码 笨办法 import os import gzip filename = sys.arg ...
- 【百奥云GS专栏】全基因组选择之工具篇
目录 1. 免费开源包/库 1.1 R包 1.2 Python库 2. 成熟软件 3. WEB/GUI工具 前面我们已经介绍了基因组选择的各类模型,今天主要来了解一下做GS有哪些可用的软件和工具.基因 ...
- CentOS6源码安装zabbix服务器
1.下载安装包并解压 2.预环境搭建 3.创建zabbix用户,编译安装zabbix 4.配置mysql 5.配置zabbix-server 6.配置apache和php 7.添加开机自启动 1 yu ...
- 微信第三方平台获取component_verify_ticket
官方文档说明: 在公众号第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL"每隔10分钟定时推送component_verify_ticket.第三方平台方在收到tic ...
- c#Gridview添加颜色
e.Row.Cells[1].ForeColor = System.Drawing.Color.Blue;
- Shell 统计文件的行数
目录 统计文件的行数 题目 题解-awk 题解-wc 题解sed 统计文件的行数 题目 写一个 bash脚本以输出一个文本文件 nowcoder.txt中的行数 示例: 假设 nowcoder.txt ...
- LeetCode二维数组中的查找
LeetCode 二维数组中的查找 题目描述 在一个 n*m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增.请完成一个搞笑的函数,输入这样的一个二维数组和一个整数,判断数 ...
- 学习java 7.2
学习内容:案例一:斐波那契数列从1开始作为第一个数,求第20个数 public class Test { public static void main(String[ ] args){ int[ ] ...