Web聊天室的实现
Tornado普通方式实现聊天室
普通的http方式连接的话,基本思路是前端页面通过JS重复连接后端服务器.
核心文件:app.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import time
import json
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render('index.html')
li = [
{'id':123123, 'content': 'asdfasdfasdf'}
]
class MsgHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
index = self.get_argument('index')
index = int(index)
if index == 0:
self.write(json.dumps(li))
else:
self.write(json.dumps(li[index:]))
settings = {
'template_path': 'views',
'static_path': 'static',
}
application = tornado.web.Application([
(r"/index.html", IndexHandler),
(r"/msg.html", MsgHandler),
], **settings)
if __name__ == "__main__":
print('http://127.0.0.1:8006')
application.listen(8006)
tornado.ioloop.IOLoop.instance().start()
index.html聊天页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
INDEX = 0;
function get_message(){
$.ajax({
url: '/msg.html',
type: 'get',
data: {'index': INDEX},
dataType: 'json',
success: function (arg) {
$.each(arg, function(k,v){
INDEX +=1;
var tag = document.createElement('p');
tag.innerHTML = v.content;
$('#container').append(tag);
});
get_message()
}
})
}
get_message()
</script>
</body>
</html>
tornado异步非阻塞方式
主要处理文件:
import logging
import tornado.escape
import tornado.ioloop
import tornado.web
import os.path
import uuid
from tornado.concurrent import Future
from tornado import gen
from tornado.options import define, options, parse_command_line
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=False, help="run in debug mode")
class MessageBuffer(object):
def __init__(self):
self.waiters = set()
self.cache = []
self.cache_size = 200
def wait_for_messages(self, cursor=None):
# Construct a Future to return to our caller. This allows
# wait_for_messages to be yielded from a coroutine even though
# it is not a coroutine itself. We will set the result of the
# Future when results are available.
result_future = Future()
if cursor:
new_count = 0
for msg in reversed(self.cache):
if msg["id"] == cursor:
break
new_count += 1
if new_count:
result_future.set_result(self.cache[-new_count:])
return result_future
self.waiters.add(result_future)
return result_future
def cancel_wait(self, future):
self.waiters.remove(future)
# Set an empty result to unblock any coroutines waiting.
future.set_result([])
def new_messages(self, messages):
logging.info("Sending new message to %r listeners", len(self.waiters))
for future in self.waiters:
future.set_result(messages)
self.waiters = set()
self.cache.extend(messages)
if len(self.cache) > self.cache_size:
self.cache = self.cache[-self.cache_size:]
# Making this a non-singleton is left as an exercise for the reader.
global_message_buffer = MessageBuffer()
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", messages=global_message_buffer.cache)
class MessageNewHandler(tornado.web.RequestHandler):
def post(self):
message = {
"id": str(uuid.uuid4()),
"body": self.get_argument("body"),
}
# to_basestring is necessary for Python 3's json encoder,
# which doesn't accept byte strings.
message["html"] = tornado.escape.to_basestring(
self.render_string("message.html", message=message))
if self.get_argument("next", None):
self.redirect(self.get_argument("next"))
else:
self.write(message)
global_message_buffer.new_messages([message])
class MessageUpdatesHandler(tornado.web.RequestHandler):
@gen.coroutine
def post(self):
cursor = self.get_argument("cursor", None)
# Save the future returned by wait_for_messages so we can cancel
# it in wait_for_messages
self.future = global_message_buffer.wait_for_messages(cursor=cursor)
messages = yield self.future
if self.request.connection.stream.closed():
return
self.write(dict(messages=messages))
def on_connection_close(self):
global_message_buffer.cancel_wait(self.future)
def main():
parse_command_line()
app = tornado.web.Application(
[
(r"/", MainHandler),
(r"/a/message/new", MessageNewHandler),
(r"/a/message/updates", MessageUpdatesHandler),
],
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
xsrf_cookies=True,
debug=options.debug,
)
app.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()
页面index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tornado Chat Demo</title>
<link rel="stylesheet" href="{{ static_url("chat.css") }}" type="text/css">
</head>
<body>
<div id="body">
<div id="inbox">
{% for message in messages %}
{% module Template("message.html", message=message) %}
{% end %}
</div>
<div id="input">
<form action="/a/message/new" method="post" id="messageform">
<table>
<tr>
<td><input type="text" name="body" id="message" style="width:500px"></td>
<td style="padding-left:5px">
<input type="submit" value="{{ _("Post") }}">
<input type="hidden" name="next" value="{{ request.path }}">
{% module xsrf_form_html() %}
</td>
</tr>
</table>
</form>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js" type="text/javascript"></script>
<script src="{{ static_url("chat.js") }}" type="text/javascript"></script>
</body>
</html>
消息页面message.html
<div class="message" id="m{{ message["id"] }}">{% module linkify(message["body"]) %}</div>
css页面chat.css
body {
background: white;
margin: 10px;
}
body,
input {
font-family: sans-serif;
font-size: 10pt;
color: black;
}
table {
border-collapse: collapse;
border: 0;
}
td {
border: 0;
padding: 0;
}
#body {
position: absolute;
bottom: 10px;
left: 10px;
}
#input {
margin-top: 0.5em;
}
#inbox .message {
padding-top: 0.25em;
}
#nav {
float: right;
z-index: 99;
}
js处理页面chat.js
$(document).ready(function() {
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function() {};
$("#messageform").on("submit", function() {
newMessage($(this));
return false;
});
$("#messageform").on("keypress", function(e) {
if (e.keyCode == 13) {
newMessage($(this));
return false;
}
return true;
});
$("#message").select();
updater.poll();
});
function newMessage(form) {
var message = form.formToDict();
var disabled = form.find("input[type=submit]");
disabled.disable();
$.postJSON("/a/message/new", message, function(response) {
updater.showMessage(response);
if (message.id) {
form.parent().remove();
} else {
form.find("input[type=text]").val("").select();
disabled.enable();
}
});
}
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
if (callback) callback(eval("(" + response + ")"));
}, error: function(response) {
console.log("ERROR:", response);
}});
};
jQuery.fn.formToDict = function() {
var fields = this.serializeArray();
var json = {};
for (var i = 0; i < fields.length; i++) {
json[fields[i].name] = fields[i].value;
}
if (json.next) delete json.next;
return json;
};
jQuery.fn.disable = function() {
this.enable(false);
return this;
};
jQuery.fn.enable = function(opt_enable) {
if (arguments.length && !opt_enable) {
this.attr("disabled", "disabled");
} else {
this.removeAttr("disabled");
}
return this;
};
var updater = {
errorSleepTime: 500,
cursor: null,
poll: function() {
var args = {"_xsrf": getCookie("_xsrf")};
if (updater.cursor) args.cursor = updater.cursor;
$.ajax({url: "/a/message/updates", type: "POST", dataType: "text",
data: $.param(args), success: updater.onSuccess,
error: updater.onError});
},
onSuccess: function(response) {
try {
updater.newMessages(eval("(" + response + ")"));
} catch (e) {
updater.onError();
return;
}
updater.errorSleepTime = 500;
window.setTimeout(updater.poll, 0);
},
onError: function(response) {
updater.errorSleepTime *= 2;
console.log("Poll error; sleeping for", updater.errorSleepTime, "ms");
window.setTimeout(updater.poll, updater.errorSleepTime);
},
newMessages: function(response) {
if (!response.messages) return;
updater.cursor = response.cursor;
var messages = response.messages;
updater.cursor = messages[messages.length - 1].id;
console.log(messages.length, "new messages, cursor:", updater.cursor);
for (var i = 0; i < messages.length; i++) {
updater.showMessage(messages[i]);
}
},
showMessage: function(message) {
var existing = $("#m" + message.id);
if (existing.length > 0) return;
var node = $(message.html);
node.hide();
$("#inbox").append(node);
node.slideDown();
}
};
可在这下载:下载地址
Web聊天室的实现的更多相关文章
- 使用Servlet和JSP实现一个简单的Web聊天室系统
1 问题描述 利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室 实战系列
ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言 http://www.cnblogs.com/panzi/p/5742089.html ASP.NET S ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十二) 代码重构使用反射工厂解耦(一)缓存切换
前言 上一篇中,我们用了反射工厂来解除BLL和UI层耦合的问题.当然那是最简单的解决方法,再复杂一点的程序可能思路相同,但是在编程细节中需要考虑的就更多了,比如今天我在重构过程中遇到的问题.也是接下来 ...
- web聊天室
开发一个web聊天室 功能需求: 1.用户可以与好友一对一聊天 2.群聊 所需知识 1.Django 2.bootstrap 3.CSS 4.ajax 涉及到的新的知识点 1.如果设计表结构的时候,一 ...
- 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)
在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言
前端时间听一个技术朋友说 LayIM 2.0 发布了,听到这个消息抓紧去官网看了一下.(http://layim.layui.com/)哎呀呀,还要购买授权[大家支持一下哦],果断买了企业版,喜欢钻研 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取)
大家好,本篇是接上一篇 ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言 ASP.NET SignalR WebIM系列第二篇.本篇会带领大家将 LayIM ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(二) 之 ChatServer搭建,连接服务器,以及注意事项。
上篇:ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取) 上一篇我们已经完成了初步界面的搭建工作,本篇将介绍IM的核心内容 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(三) 之 实现单聊,群聊,发送图片,文件。
上篇讲解了如何搭建聊天服务器,以及客户端js怎么和layui的语法配合.服务器已经连接上了,那么聊天还会远吗? 进入正题,正如上一篇提到的我们用 Client.Group(groupId)的方法向客户 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(四) 之 用户搜索(Elasticsearch),加好友流程(1)。
前面几篇基本已经实现了大部分即时通讯功能:聊天,群聊,发送文件,图片,消息.不过这些业务都是比较粗犷的.下面我们就把业务细化,之前用的是死数据,那我们就从加好友开始吧.加好友,首先你得知道你要加谁.L ...
随机推荐
- python学习之面向对象
目录 __main__,__name__ __module__,__class__ __init__ __del__ __repr__,__str__ __mro__ __call__ __new__ ...
- centos安装nodejs和配置npm
1.下载安装nodejs 1 wget http://nodejs.org/dist/v7.4.0/node-v7.4.0.tar.gz 2 yum install gcc openssl-devel ...
- 28-SQLServer带见证服务器的镜像搭建
一.注意点 1.数据库的模式要是完整模式. 2.要对数据库完整备份和事务日志备份,分别还原到镜像库上,使用NORECOVERY模式. 3.镜像数据库是不允许删除和操作,即便查看属性也不行. 4.先删除 ...
- javaScript基础及初始面向对象
对象是什么?对象是包含相关属性和方法的集合体属性方法什么是面向对象面向对象仅仅是一个概念或者编程思想通过一种叫做原型的方式来实现面向对象编程 创建对象自定义对象内置对象 自定义对象2-1基于Objec ...
- vue:概要
一.环境 #安装nodejs-官网安装包配置环境变量 node -v #安装webpack npm install webpack -g #安装vue-cli npm install vue-cli ...
- SQL死锁处理
查询死锁 select request_session_id spid, OBJECT_NAME(resource_associated_entity_id) tableName from sys.d ...
- 入门指引之查看accesstoken
1. 测试工具查看 https://mp.weixin.qq.com/debug/ 2. 代码获取: # -*- coding: utf-8 -*- # filename: basic.py impo ...
- Elasticsearch 调优之 搜索速度优化
本章讨论搜索速度优化:搜索速度与系统资源.数据索引方式.查询方式等多方面 1.为文件系统cache预留足够的内存 1)应用程序一般情况下,读写都会被操作系统“cache” 2)cache保存在物理内存 ...
- 2018 Benelux Algorithm Programming Contest (BAPC 18)
目录 Contest Info Solutions A A Prize No One Can Win B Birthday Boy C Cardboard Container D Driver Dis ...
- 代码 | 自适应大邻域搜索系列之(5) - ALNS_Iteration_Status和ALNS_Parameters的代码解析
前言 上一篇推文说了,后面的代码难度直线下降,各位小伙伴可以放去n的100次方心了.今天讲讲一些细枝末节,就是前面一直有提到的参数和一些状态的记录代码.这个简单啦,小编也不作过多解释了.大家直接看代码 ...