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. NLP获取词向量的方法(Glove、n-gram、word2vec、fastText、ELMo 对比分析)

    自然语言处理的第一步就是获取词向量,获取词向量的方法总体可以分为两种两种,一个是基于统计方法的,一种是基于语言模型的. 1 Glove - 基于统计方法 Glove是一个典型的基于统计的获取词向量的方 ...

  2. 难道你还不知道Spring之事务的回滚和提交的原理吗,这篇文章带你走进源码级别的解读。

    上一篇文章讲解了获取事务,并通过获取的connection设置只读,隔离级别等:这篇文章讲事务剩下的回滚和提交. 事务的回滚处理 之前已经完成了目标方法运行前的事务准备工作.而这些准备工作的最大目的无 ...

  3. 【POJ3349 Snowflake Snow Snowflakes】【Hash表】

    最近在对照省选知识点自己的技能树 今天是Hash 题面 大概是给定有n个6元序列 定义两个序列相等 当两个序列各自从某一个元素开始顺时针或者逆时针旋转排列能得到两个相同的序列 求这n个6元序列中是否有 ...

  4. AtCoder Beginner Contest 188题解

    A 题意 问\(x,y\)相差是否小于\(3\) #include<iostream> #include<cstdio> #include<cmath> #defi ...

  5. 洛谷 P7360 -「JZOI-1」红包(Min-Max 容斥+推式子)

    洛谷题面传送门 hot tea. 首先注意到这个 \(\text{lcm}\) 特别棘手,并且这里的 \(k\) 大得离谱,我们也没办法直接枚举每个质因子的贡献来计算答案.不过考虑到如果我们把这里的 ...

  6. 洛谷 P4437 [HNOI/AHOI2018]排列(贪心+堆,思维题)

    题面传送门 开始 WA ycx 的遗产(bushi 首先可以将题目转化为图论模型:\(\forall i\) 连边 \(a_i\to i\),然后求图的一个拓扑序 \(b_1,b_2,\dots b_ ...

  7. Perl语言入门10-13

    ----------第十章 其他控制结构---------------- unless结构 unless($fred =~ /\A[A-Z_\w*\z]/i){print "yes" ...

  8. 【机器学习与R语言】3-概率学习朴素贝叶斯(NB)

    目录 1.理解朴素贝叶斯 1)基本概念 2)朴素贝叶斯算法 2.朴素贝斯分类应用 1)收集数据 2)探索和准备数据 3)训练模型 4)评估模型性能 5)提升模型性能 1.理解朴素贝叶斯 1)基本概念 ...

  9. 【R】表达矩阵指定绘制两样本的相关性散点图?

    表达矩阵 要做两两样本的相关性散点图,并计算标明相关系数. 编写函数要点: 直接在aes中传参是不行的 线性回归表达式 函数 方法1:用!!ensym myplot <- function(in ...

  10. 【Workflows】 WGS/WES Mapping to Variant Calls

    WGS/WES Mapping to Variant Calls - Version 1.0 htslib官网上给的一个WGS/WES的流程.关于htslib.samtools和bcftools之间的 ...