Tornado实现了对socket的封装:tornado.web.RequestHandler

工程目录:

1、主程序 manage.py

import tornado.web
import tornado.httpserver
from tornado.options import define, options, parse_command_line from chat.views import IndexHandler, LoginHandler, ChatHandler
from util.settings import TEMPLATE_PATH, STATIC_PATH define("port", default=8180, help='run on the port', type=int) def make_app():
return tornado.web.Application(handlers=[
(r'/', IndexHandler),
(r'/login', LoginHandler),
(r'/chat', ChatHandler),
],
pycket={
'engine': 'redis',
'storage': {
'host': 'fot.redis.cache.net',
'port': 6379,
'password': 'yKigE3ZF0mGBSP4/M=',
'db_sessions': 5,
'db_notifications': 11,
'max_connections': 2 ** 31,
},
'cookies': {
'expires_days': 30,
'max_age': 100
},
},
login_url='/login',
template_path=TEMPLATE_PATH,
static_path=STATIC_PATH,
debug=True,
cookie_secret='cqVJzSSjQgWzKtpHMd4NaSeEa6yTy0qRicyeUDIMSjo='
) if __name__ == '__main__':
tornado.options.parse_command_line()
app = make_app()
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

2、配置 settings.py

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates')

STATIC_PATH = os.path.join(BASE_DIR, 'static')

3、聊天程序 views.py

# -*- coding: utf-8 -*-
import datetime
import json import tornado.web
import tornado.websocket
from tornado.web import authenticated # 导入装饰器
from pycket.session import SessionMixin # 设置BaseHandler类,重写函数get_current_user
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self): # 前面有绿色小圆圈带个o,再加一个箭头表示重写
current_user = self.session.get('user') # 获取加密的cookie
if current_user:
return current_user
return None # 基类
class BaseWebSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin):
def get_current_user(self):
current_user = self.session.get('user') if current_user:
return current_user
return None # 跳转
class IndexHandler(BaseHandler):
@authenticated # 内置装饰器,检查是否登录
def get(self):
self.render('chat.html') class LoginHandler(BaseHandler):
def get(self):
self.render('index.html') # 跳转页面带上获取的参数 def post(self, *args, **kwargs):
user = self.get_argument('nickname', '')
if user: self.session.set('user', user) # 设置加密cookie
self.redirect('/') # 跳转到之前的路由
else:
self.render('index.html') class ChatHandler(BaseWebSocketHandler):
# 定义接收/发送聊天消息的视图处理类,继承自websocket的WebSocketHandler
# 定义一个集合,用来保存在线的所有用户 online_users = set() # 从客户端获取cookie信息 # 重写open方法,当有新的聊天用户进入的时候自动触发该函数
def open(self): # 新用户上线,加入集合
self.online_users.add(self)
# 将新用户加入的信息发送给所有用户 for user in self.online_users:
user.write_message('[%s]join room' % self.current_user) # 重写on_message方法,当聊天消息有更新时自动触发的函数
def on_message(self, message):
msgobj = {'msg': message} for user in self.online_users:
msgobj['key'] = '%s-%s-sea: ' % (self.current_user, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
user.write_message(json.dumps(msgobj)) # 重写on_close方法,当有用户离开时自动触发的函数
def on_close(self):
# 移除用户
self.online_users.remove(self)
for user in self.online_users:
user.write_message('[%s]remove room' % self.current_user) # 重写check_origin方法, 解决WebSocket的跨域请求
def check_origin(self, origin):
return True

4、前端登录 index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室登录首页</title>
<script src="../static/jquery-3.4.1.js"></script>
</head>
<body>
<div>
<div style="width:60%;">
<div>
聊天室个人登录
</div>
<div>
<form method="post" action="/login" style="width:80%">
<p>昵称:<input type="text" placeholder="请输入昵称" name="nickname"></p>
<button type="submit">登录</button>
</form>
</div>
</div>
</div>
</body>
</html>

5、前端聊天室 chat.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> WebSocket </title>
<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/jquery-3.4.1.js"></script>
<script type="text/javascript">
var mes = document.getElementById('messages');
var wsUrl = "ws://"+ window.location.host +"/chat";
var Socket = ''; if('WebSocket' in window){ /*判断浏览器是否支持WebSocket接口*/
/*创建创建 WebSocket 对象,协议本身使用新的ws://URL格式*/ createWebSocket();
}else{
/*浏览器不支持 WebSocket*/
alert("您的浏览器不支持 WebSocket!");
} function createWebSocket() {
try {
Socket = new WebSocket(wsUrl); init();
} catch(e) {
console.log('catch');
reconnect(wsUrl); //调用心跳
}
} function init() {
/*连接建立时触发*/
Socket.onopen = function () {
alert("连接已建立,可以进行通信"); heartCheck.start(); //调用心跳
};
/*客户端接收服务端数据时触发*/
Socket.onmessage = function (ev) {
var received_msg = ev.data; /*接受消息*/
var jopmsg = '';
try {
received_msg = JSON.parse(received_msg);
console.log(received_msg['msg']); if(received_msg['msg'] == '121')
jopmsg = '121'; received_msg = received_msg['key'] + received_msg['msg'];
}catch (e) { } //发送信息为121时为心跳,不记录到页面(只是个约定)
if(jopmsg !== '121'){
var aLi = "<li>" + received_msg + "</li>";
mes.innerHTML += aLi;
} heartCheck.start(); //调用心跳
};
/*连接关闭时触发*/
Socket.onclose = function () {
mes.innerHTML += "<br>连接已经关闭..."; reconnect(wsUrl); //关闭连接重新连接
};
} function WebSocketSend() {
/*form 里的Dom元素(input select checkbox textarea radio)都是value*/
var send_msg = document.getElementById('text').value;
//或者JQ中获取
// var send_msg = $("#text").val();
/*使用连接发送消息*/
Socket.send(send_msg);
$("#text").val('');
} var lockReconnect = false;//避免重复连接
function reconnect(url) {
if(lockReconnect) {
return true;
}; lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
setTimeout(function () {
createWebSocket(url);
lockReconnect = false;
}, 5000); } //心跳检测
var heartCheck = {
timeout: 10000, //每隔三秒发送心跳
num: 3, //3次心跳均未响应重连
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
var _this = this;
var _num = this.num;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
Socket.send("121"); // 心跳包
_num--;
//计算答复的超时次数
if(_num === 0) {
Socket.colse();
}
}, this.timeout)
}
} </script>
</body>
</html>

6、运行效果: 输入 http://127.0.0.1:8180

7、部署到线上参考:https://www.cnblogs.com/cj8988/p/11288892.html

注 :nginx需要添加一个配置 (在 server {} 里添加下面配置)

    location /chat {
proxy_pass http://tornados;
proxy_http_version 1.1;
proxy_connect_timeout 4s; #配置点1
proxy_read_timeout 120s; #配置点2,如果没效,可以考虑这个时间配置长一点
proxy_send_timeout 120s;

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

8、注意,由于nginx超时问题,过段时间websocket会自动断开,所有前端需要设置心跳。

前端 chat.html 中   :

   //心跳检测
var heartCheck = {
timeout: 10000, //每隔三秒发送心跳
num: 3, //3次心跳均未响应重连
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
var _this = this;
var _num = this.num;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
Socket.send("121"); // 心跳包
_num--;
//计算答复的超时次数
if(_num === 0) {
Socket.colse();
}
}, this.timeout)
}
}

在需要的地方调用:

heartCheck.start();

参考文档:
  https://www.jianshu.com/p/93b1788f055c
    
  https://www.lishuaishuai.com/html/759.html
  https://www.cnblogs.com/cj8988/p/11288892.html

Tornado WebSocket简单聊天的更多相关文章

  1. python tornado websocket 多聊天室(返回消息给部分连接者)

    python tornado 构建多个聊天室, 多个聊天室之间相互独立, 实现服务器端将消息返回给相应的部分客户端! chatHome.py // 服务器端, 渲染主页 --> 聊天室建立web ...

  2. [tornado]websocket 最简单demo

    想法 前两天想看看django 长轮询或者是websocket的方案,发现都不太好使. tornado很适合做这个工作,于是找了些资料,参照了做了个最简单demo,以便备用. 具体的概念就不说了,to ...

  3. tornado websocket聊天室

    1.app.py #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json import tornado.ioloop ...

  4. websocket简单入门

    今天说起及时通信的时候,突然被问到时用推的方式,还是定时接受的方式,由于之前页面都是用传统的ajax处理,可能对ajax的定时获取根深蒂固了,所以一时之间没有相同怎么会出现推的方式呢?当被提及webs ...

  5. SpringBoot 搭建简单聊天室

    SpringBoot 搭建简单聊天室(queue 点对点) 1.引用 SpringBoot 搭建 WebSocket 链接 https://www.cnblogs.com/yi1036943655/p ...

  6. 利用socket.io+nodejs打造简单聊天室

    代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...

  7. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  8. Websocket 简单对话:静态网页与pycharm对话

    WebSocket websocket 是一种在单个Tcp连接上进行双全工通信的协议.websocket通信协议于2011年被IETF定为标准RFC6455,并 由RFc7936补充规范.WebSoc ...

  9. Tornado项目简单创建

    Tornado是使用Python编写的一个强大的.可扩展的Web服务器.它在处理严峻的网络流量时表现得足够强健,但却在创建和编写时有着足够的轻量级,并能够被用在大量的应用和工具中. tornado技术 ...

随机推荐

  1. codeforces405D

    Toy Sum CodeForces - 405D Little Chris is very keen on his toy blocks. His teacher, however, wants C ...

  2. 使用pyinstaller 打包python程序

    1.打开PyCharm的Terminal,使用命令pip install pyinstaller安装pyinstaller 2.打包命令:pyinstaller --console --onefile ...

  3. Kbengine游戏引擎-【1】kbengine安装

    本文主要介绍如何在Linux上安装 官网环境要求:Centos >= 5.x, Debian >= 5.x GCC版本: >= 4.4.x 官网链接 本文的安装环境介绍:Centos ...

  4. 框架-Eureka:百科

    ylbtech-框架-Eureka:百科 1.返回顶部 1. Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中 ...

  5. 解决ssh连接超时(ssh timeout)的方法

    echo export TMOUT=1000000 >> /root/.bash_profile (可设置为-1为永不超时) cat /root/.bash_profile source ...

  6. 加载自定义目录下的springmvc.xml配置文件

    在默认情况下:springmvc框架的配置文件必须叫<servlet-name>-servlet.xml 且必须放在/WEB-INF/目录下,我们可以在web.xml文件中,为Dispat ...

  7. JavaScript中字符串,数组的基本操作

    JavaScript的字符串就是用”或”“括起来的字符表示. js中操作字符串: 1.获得字符串的长度 var s = 'Hello, world!'; s.length; // 132.获取指定字符 ...

  8. Understanding decimal(p, s) of sqlite3

    带固定精度和小数位数的数值数据类型.decimal(p[ ,s]) 和 numeric(p[ ,s]) 固定精度和小数位数. 使用最大精度时,有效值的范围为 - 10^38 +1 到 10^38 - ...

  9. 互斥锁lock、信号量semaphore、事件Event、

    1.互斥锁lock 应用在多进程中互斥所lock:互斥锁是进程间的get_ticket互相排斥进程之间,谁先枪占到资源,谁就先上锁,等到解锁之后,下一个进程在继续使用.# 语法: 上锁: lock.a ...

  10. IDEA快捷键(修改成eclipse版)+Templates

    快捷键:使用快捷键需要下载改建的配置文件,默认eclipse版的按键还是不全的. 链接:https://pan.baidu.com/s/17H4tFh__k6rExGpAf8NRJg 密码:rnl3 ...