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与服务器通讯的常用方法如下:

  1. var ws = new WebSocket("ws://127.0.0.1:8888/websocket"); // 新建一个ws连接
  2. ws.onopen = function() { // 连接建立好后的回调
  3. ws.send("Hello, world"); // 向建立的连接发送消息
  4. };
  5. ws.onmessage = function (evt) { // 收到服务器发送的消息后执行的回调
  6. alert(evt.data); // 接收的消息内容在事件参数evt的data属性中
  7. };

  

3. 在线聊天室的小Demo

后端代码 server.py

  1. import tornado.web
  2. import tornado.ioloop
  3. import tornado.httpserver
  4. import tornado.options
  5. import os,json
  6. import datetime
  7.  
  8. from tornado.web import RequestHandler
  9. from tornado.options import define, options
  10. from tornado.websocket import WebSocketHandler
  11.  
  12. class IndexHandler(RequestHandler):
  13. def get_current_user(self):
  14. '''
  15. 重写RequestHandler类中的get_current_user方法,用来判断当前是否是登录状态,请求中所有被@tornado.web.authenticated 装饰的方法,都需要此方法返回值不为None,否则给与403拒绝
  16. :return: 用户名或者None . 为None判断为非法请求,POST 时Tornado进行403禁止访问 ;GET 时 302 重定向到/login
  17. '''
  18. user = self.get_argument(name='username',default='None')
  19. if user and user != 'None':
  20. print('IndexHandler类 get_current_user获取到用户:',user)
  21. return user
  22.  
  23. @tornado.web.authenticated #确认请求合法 依赖于get_current_user(self):函数的返回值作为判断请求是否合法
  24. def get(self):
  25. print("IndexHandler 收到GET请求")
  26. self.render("online_index.html",current_user=self.current_user)
  27.  
  28. @tornado.web.authenticated
  29. def post(self, *args, **kwargs):
  30. print('IndexHandler 收到POST请求')
  31. self.render("online_index.html", current_user=self.current_user)
  32.  
  33. class LoginHandler(RequestHandler):
  34. def get(self, *args, **kwargs):
  35. '''
  36. 处理输入昵称界面get请求
  37. :param args:
  38. :param kwargs:
  39. :return:
  40. '''
  41. cookie_value = self.get_secure_cookie('count')
  42. print('cookie_value :', cookie_value)
  43. count = int(cookie_value) + 1 if cookie_value else 1
  44. self.set_secure_cookie("count", str(count)) # 设置一个带签名和时间戳的cookie,防止cookie被伪造。
  45.  
  46. #使用ajax方法做的前端
  47. # self.render('login_use_ajax.html')
  48. #使用form表单提交数据 的前端
  49. self.render('login_use_form.html')
  50. def post(self, *args, **kwargs):
  51. '''
  52. 暂时用不到
  53. :param args:
  54. :param kwargs:
  55. :return:
  56. '''
  57. pass
  58.  
  59. # 继承tornado.websocket.WebSocketHandler,只处理WS协议的请求
  60. class ChatHandler(WebSocketHandler):
  61. def get_current_user(self):
  62. user = self.get_argument(name='username',default='None')
  63. if user and user != 'None':
  64. return user
  65.  
  66. users = set() # 用来存放在线用户的容器
  67. @tornado.web.authenticated
  68. def open(self):
  69. print('收到新的WebSocket连接')
  70. self.users.add(self) # 建立连接后添加用户到容器中
  71. for u in self.users: # 向已在线用户发送消息
  72. u .write_message(u"[%s]-[%s]-%s 进入聊天室" % (
  73. self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self.current_user))
  74.  
  75. def on_message(self, message):
  76. message = json.loads(message)
  77. print(type(message),message)
  78. for u in self.users: # 向在线用户广播消息
  79. u.write_message(u"[%s]-[%s]-说:<br> &nbsp&nbsp&nbsp&nbsp%s" % ( datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self.current_user,message.get('msg')))
  80.  
  81. def on_close(self):
  82. self.users.remove(self) # 用户关闭连接后从容器中移除用户
  83. for u in self.users:
  84. u.write_message(u"[%s]-[%s]-%s 离开聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),self.current_user))
  85.  
  86. def check_origin(self, origin):
  87. return True # 允许WebSocket的跨域请求
  88.  
  89. if __name__ == '__main__':
  90. tornado.options.parse_command_line() #允许命令行启动程序
  91.  
  92. app = tornado.web.Application([ #定义处理web请求的应用程序
  93. (r"/", IndexHandler),
  94. (r"/login", LoginHandler),
  95. (r"/chat", ChatHandler), # 处理WebSocket协议传输的数据
  96. ],
         websocket_ping_interval = 5,     # WebSocket ping探活包发送间隔秒数
  97. static_path = os.path.join(os.path.dirname(__file__), "statics"), #配置应用程序前端所需静态文件目录
  98. template_path = os.path.join(os.path.dirname(__file__), "templates"), #配置html文件路径
  99. login_url='/login', #配置登录url
  100. xsrf_cookies=True, #,防止跨站请求攻击,在post请求中起效,
  101. cookie_secret="2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A=", # 安全cookie用预置秘钥 base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
  102. debug = True #配置调试级别
  103. )
  104. http_server = tornado.httpserver.HTTPServer(app) #将应用处理逻辑 传递给HTTPServer 服务
  105. define("port", default=8000, type=int) #设置一个监听地址
  106. http_server.listen(options.port) #配置监听地址到 HTTPServe
  107. tornado.ioloop.IOLoop.current().start() #启动应用

前端代码online_index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>聊天室</title>
  6. </head>
  7. <body>
  8. <div id="contents" style="height:500px;overflow:auto; border:green solid 2px"></div>
  9. <div>
  10. <textarea id="msg" style="width: 260px;height: 120px" placeholder="输入要发送的内容"></textarea>
  11. <input id="send_msg" type="button" href="javascript:;" onclick="sendMsg()" value="发送">
  12. </div>
  13. <script src="{{static_url('js/jquery.min.js')}}"></script>
  14. <script type="text/javascript">
  15.  
  16. //创建ws协议连接,创建出来后就已经和服务端建立连接
  17. var ws = new WebSocket("ws://127.0.0.1:8000/chat?username={{current_user}}");
  18.  
  19. //收到ws消息的时候在div对话框里面增加一行文字
  20. ws.onmessage = function(recv) {
  21. $("#contents").append("<p>" + recv.data + "</p>");
  22. };
  23.  
  24. var username = "{{current_user}}";
  25. /////////////////////////监控按下回车键动作//////////////////////////
  26. // $("#send_msg").keypress(
  27. // function () {
  28. // console.log('发送消息');
  29. // sendMsg();
  30. // }
  31. // )
  32. ///////////////////////////////////////////////////////////////////////
  33. function sendMsg() {
  34. $('#msg').val($('#msg').val().trim());//去除输入前后的空格
  35. var msg = $("#msg").val();
  36. if ( msg && msg.length >0) {
  37. data = {
  38. // username:username,
  39. msg:msg
  40. };
  41. ws.send(JSON.stringify(data));
  42. $("#msg").val("");
  43. }else {
  44. alert('内容不许为空')
  45. }
  46. }
  47. </script>
  48. </body>
  49. </html>

前端login_use_form登录界面代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录聊天室</title>
  6. <script src="{{static_url('js/jquery.min.js')}}"></script>
  7. </head>
  8. <body>
  9. <!--使用form表单获取登录页-->
  10. <form action="/" method="post" onsubmit="return check_user()">
  11. {% module xsrf_form_html() %}
  12. <input id="username" name="username" placeholder="配置昵称,即可聊天">
  13. <input id="conform_username" type="submit" value="确认昵称" >
  14. </form>
  15.  
  16. <script>
  17. function check_user(){
  18. $('#username').val($('#username').val().trim());//去除输入前后的空格
  19. var username = $('#username').val();
  20. if ( !username ){
  21. alert('昵称不能为空');
  22. return false;
  23. }else {
  24. alert('昵称OK');
  25. return true
  26. }
  27. }
  28. </script>
  29.  
  30. </body>
  31. </html>

Tornado 之 WebSocket的更多相关文章

  1. tornado之WebSocket

    WebSocket WebSocket是HTML5规范中新提出的客户端-服务器通讯协议,协议本身使用新的ws://URL格式. WebSocket 是独立的.创建在 TCP 上的协议,和 HTTP 的 ...

  2. tornado+websocket+mongodb实现在线视屏文字聊天

    最近学了tornado和mongo,所以结合websocket 实现一个聊天功能,从而加深一下相关知识点的印象 1.websocket概览 webscoket是一种全双工通信模式的协议,客户端连接服务 ...

  3. Tornado长轮询和WebSocket

    Http协议是一种请求响应式协议, 不允许服务端主动向客户端发送信息. 短轮询是一种简单的实现服务端推送消息的解决方案, 客户端以一定间隔自动向服务端发送刷新请求, 服务端返回要推送的消息作为响应. ...

  4. tornado WebSocket详解

    1.什么是WebSocketwebsocket和长轮询的区别是客户端和服务器之间是持久连接的双向通信.协议使用ws://URL格式,但它在是在标准HTTP上实现的. 2.tornado的WebSock ...

  5. 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

    http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...

  6. websocket介绍

    websocket应用 手动实现的websocket 你所见过的websocket 你一定见过在网站中,有一个游客聊天的聊天框,比如人人影视.这个聊天框是如何实现即时通讯的呢,就是用到了websock ...

  7. 你真的会websocket吗

    Websocket WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端. WebSocket通信协议于2 ...

  8. 异步与websocket

    异步与WebSockets 知识点 理解同步与异步执行过程 理解异步代码的回调写法与yield写法 Tornado异步 异步Web客户端AsyncHTTPClient tornado.web.asyn ...

  9. websocket基本概念

    (1)websocket 协议 与 http协议 websocket 协议,双工通道 socket连接不断开 http 协议 ,只能请求响应 (用户主动获取) socket连接断开 都是基于socke ...

随机推荐

  1. python网络自动化运维之telnetlib实验(EVE-NG+pycharm)

    运行环境: 物理机:win10 1903 网络设备:EVE-NG模拟器上运行思科三层路由器 网络设备OS版本:cisco ios(versions 15.6) python环境:pycharm 3.3 ...

  2. Codeforces 1466G - Song of the Sirens(哈希)

    Codeforces 题面传送门 & 洛谷题面传送门 事实证明,有的难度评分不算很高.涉及的知识点不算很难的题目也能出得非常神仙 首先考虑如何暴力求答案.注意到一个文本串 \(T\) 在 \( ...

  3. Python如何支持读入gz压缩或未压缩文件?

    目录 需求 示例代码 笨办法 Pythonic方法 需求 要写一个接口,同时支持压缩和未压缩文件读入 示例代码 笨办法 import os import gzip filename = sys.arg ...

  4. 【百奥云GS专栏】全基因组选择之工具篇

    目录 1. 免费开源包/库 1.1 R包 1.2 Python库 2. 成熟软件 3. WEB/GUI工具 前面我们已经介绍了基因组选择的各类模型,今天主要来了解一下做GS有哪些可用的软件和工具.基因 ...

  5. CentOS6源码安装zabbix服务器

    1.下载安装包并解压 2.预环境搭建 3.创建zabbix用户,编译安装zabbix 4.配置mysql 5.配置zabbix-server 6.配置apache和php 7.添加开机自启动 1 yu ...

  6. 微信第三方平台获取component_verify_ticket

    官方文档说明: 在公众号第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL"每隔10分钟定时推送component_verify_ticket.第三方平台方在收到tic ...

  7. c#Gridview添加颜色

    e.Row.Cells[1].ForeColor = System.Drawing.Color.Blue;

  8. Shell 统计文件的行数

    目录 统计文件的行数 题目 题解-awk 题解-wc 题解sed 统计文件的行数 题目 写一个 bash脚本以输出一个文本文件 nowcoder.txt中的行数 示例: 假设 nowcoder.txt ...

  9. LeetCode二维数组中的查找

    LeetCode 二维数组中的查找 题目描述 在一个 n*m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增.请完成一个搞笑的函数,输入这样的一个二维数组和一个整数,判断数 ...

  10. 学习java 7.2

    学习内容:案例一:斐波那契数列从1开始作为第一个数,求第20个数 public class Test { public static void main(String[ ] args){ int[ ] ...