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聊天室的实现的更多相关文章

  1. 使用Servlet和JSP实现一个简单的Web聊天室系统

    1 问题描述                                                利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...

  2. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室 实战系列

    ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言  http://www.cnblogs.com/panzi/p/5742089.html ASP.NET S ...

  3. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十二) 代码重构使用反射工厂解耦(一)缓存切换

    前言 上一篇中,我们用了反射工厂来解除BLL和UI层耦合的问题.当然那是最简单的解决方法,再复杂一点的程序可能思路相同,但是在编程细节中需要考虑的就更多了,比如今天我在重构过程中遇到的问题.也是接下来 ...

  4. web聊天室

    开发一个web聊天室 功能需求: 1.用户可以与好友一对一聊天 2.群聊 所需知识 1.Django 2.bootstrap 3.CSS 4.ajax 涉及到的新的知识点 1.如果设计表结构的时候,一 ...

  5. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  6. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言

    前端时间听一个技术朋友说 LayIM 2.0 发布了,听到这个消息抓紧去官网看了一下.(http://layim.layui.com/)哎呀呀,还要购买授权[大家支持一下哦],果断买了企业版,喜欢钻研 ...

  7. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取)

    大家好,本篇是接上一篇 ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言  ASP.NET SignalR WebIM系列第二篇.本篇会带领大家将 LayIM ...

  8. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(二) 之 ChatServer搭建,连接服务器,以及注意事项。

    上篇:ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取) 上一篇我们已经完成了初步界面的搭建工作,本篇将介绍IM的核心内容 ...

  9. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(三) 之 实现单聊,群聊,发送图片,文件。

    上篇讲解了如何搭建聊天服务器,以及客户端js怎么和layui的语法配合.服务器已经连接上了,那么聊天还会远吗? 进入正题,正如上一篇提到的我们用 Client.Group(groupId)的方法向客户 ...

  10. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(四) 之 用户搜索(Elasticsearch),加好友流程(1)。

    前面几篇基本已经实现了大部分即时通讯功能:聊天,群聊,发送文件,图片,消息.不过这些业务都是比较粗犷的.下面我们就把业务细化,之前用的是死数据,那我们就从加好友开始吧.加好友,首先你得知道你要加谁.L ...

随机推荐

  1. linux网络编程之简单的线程池实现

    转眼间离15年的春节越来越近了,还有两周的工作时间貌似心已经不在异乡了,期待与家人团聚的日子,当然最后两周也得坚持站好最后一班岗,另外期待的日子往往是心里不能平静的,越是想着过年,反而日子过得越慢,于 ...

  2. 【BZOJ3691】游行 最小可相交路径覆盖转化

    因为C是不断变化的而且C是和点权相关和边权无关 所以我们可以MCMF但是MCMF的时候不能与C相关 再分析问题 我们可以认为每条路径S->T只覆盖T这个终点 因为题目中说了如果Si != Ti ...

  3. seo与python大数据结合给文本分词并提取高频词

    最近研究seo和python如何结合,参考网上的一些资料,写的这个程序. 目的:分析某个行业(例如:圆柱模板)用户最关心的一些词,根据需求去自动调整TDK,以及栏目,内容页的规划 使用方法: 1.下载 ...

  4. SQL SERVER表变量和临时表

    一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯一约束,NULL约束和CHECK约束(外键约 ...

  5. 使用openoffice转pdf,详细

    期由于项目的需求,需要word文档转pdf在线预览,由于一直没有接触这块,所以花了将近四天时间才弄明白. 写这篇文章的主要目的是加深自己的记忆,同时方便以后在用. (最近有使用了这个功能,发现这篇文章 ...

  6. Spring第二次案例和AOP

    Spring加上AOP com.mapper.entity.UserInfo package com.Spring.entity; public class UserInfo { private In ...

  7. 靠边的列表如果没有设置margin-left:20px,那么是看不到列表序号的。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. MutationObserverAPI--微任务

    1. 作用 MutationObserverAPI可以看作一个监听DOM所有变化(包含节点.属性.文本内容的变动)的接口. 和EventTargetAPI的addEventListener相比: 共同 ...

  9. jQuery 中的事件和动画

    一.jQuery中的事件 1.加载DOM 以浏览器装载文档为例,在页面加载完毕后,浏览器会通过JavaScript为DOM元素添加事件.在常规JavaScript代码中,通常使用window.onlo ...

  10. react页面跳转 window.location.href和window.open的几种用法和区别

    https://www.cnblogs.com/Qian123/p/5345298.html