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> &nbsp&nbsp&nbsp&nbsp%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的更多相关文章

  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. 洛谷 P4135 作诗(分块)

    题目链接 题意:\(n\) 个数,每个数都在 \([1,c]\) 中,\(m\) 次询问,每次问在 \([l,r]\) 中有多少个数出现偶数次.强制在线. \(1 \leq n,m,c \leq 10 ...

  2. 混合(Pooling)样本测序研究

    目录 1.混合测序基础 2. 点突变检测 3. BSA 4. BSR 5. 混合样本GWAS分析 6. 混合样本驯化研究 7. 小结 1.混合测序基础 测序成本虽然下降了,但对于植物育种应用研究来说还 ...

  3. DRF请求流程及主要模块分析

    目录 Django中CBV请求生命周期 drf前期准备 1. 在views.py中视图类继承drf的APIView类 2. drf的as_view()方法 drf主要模块分析 1. 请求模块 2. 渲 ...

  4. Oracle-where exists()、not exists() 、in()、not in()用法以及效率差异

    0.exists() 用法: select * from T1 where exists(select 1 from T2 where T1.a=T2.a) 其中 "select 1 fro ...

  5. Python3调用C程序(超详解)

    Python3调用C程序(超详解) Python为什么要调用C? 1.要提高代码的运算速度,C比Python快50倍以上 2.对于C语言里很多传统类库,不想用Python重写,想对从内存到文件接口这样 ...

  6. c#GridView

    分页: 1.先把属性AllowPaging设置为true, 2.pagesize为每一页的行数,PageSize="15". 3.OnPageIndexChanging=" ...

  7. Linux基础命令---ntpq查询时间服务器

    ntpq ntpq指令使用NTP模式6数据包与NTP服务器通信,能够在允许的网络上查询的兼容的服务器.它以交互模式运行,或者通过命令行参数运行. 此命令的适用范围:RedHat.RHEL.Ubuntu ...

  8. Mysql的索引调优详解:如何去创建索引以及避免索引失效

    在正式介绍Mysql调优之前,先补充mysql的两种引擎 mysql逻辑分层 InnoDB:事务优先(适合高并发操作,行锁) MyISAM:性能优先(表锁) 查看使用的引擎: show variabl ...

  9. 监控Linux服务器网站状态的SHELL脚本

    1,监控httpd状态码的shell脚本代码. #!/bin/sh #site: www.jquerycn.cn # website[0]=www.jquerycn.cn/chuzu/' #网站1 m ...

  10. js - 日期、时间 Date对象方法

    Date 是 JS 内置的日期构造函数 var d = new Date();  // 这个是系统当前时间的日期实例 d.getYear(); // 返回 d 实例年份 - 1900 d.getFul ...