大家好,下面我和大家分享一下非诚勿扰tornado的实现方式。
非诚勿扰APK地址: http://as.baidu.com/a/item?docid=475931&f=web_alad_1
概念介绍:
Comet:基于HTTP长连接的“服务器推”技术
基于Comet的技术主要分为流(streaming)方式和长轮询(long-polling)方式。
首先看Comet这个单词,很多地方都会说到,它是“彗星”的意思,顾名思义,彗星有个长长的尾巴,以此来说明客户端发起的请求是长连的。即用户发起请求后就挂起,等待服务器返回数据,在此期间不会断开连接。流方式和长轮询方式的区别就是:对于流方式,客户端发起连接就不会断开连接,而是由服务器端进行控制。当服务器端有更新时,刷新数据,客户端进行更新;而对于长轮询,当服务器端有更新返回,客户端先断开连接,进行处理,然后重新发起连接。
长轮询(long-polling)方式
流(streaming)方式
根据上面两种方式,我使用了Streaming的方式,原因很简单,在移动互联网的网络环境不如互联网的网络环境,建立tcp链接的开销比较大,所以我们使用streaming方式,在客户端意外断开链接后进行重链接。
具体的实现,其实实现起来
class Chat(object):
"""Chat Redis Server"""
def __init__(self):
redis_settings = settings.redis_settings
config = redis_settings["REDIS_CHAT"]
self.redis = Redis(config["servers"], config["port"], config["db"])
def chat_list_key(self, from_user, to_user):
return "chat_messages:%s:%s" % (from_user.id, to_user.id)
def say(self, from_user, to_user, msg, img,sys='', urla="",urlb=""):
"""chat to some one"""
#add unread list
pipeline = self.redis.pipeline()
try:
#add msg to unread list
chat_list_name = self.chat_list_key(from_user, to_user)
pipeline.rpush(chat_list_name, msg)
pipeline.execute()
except:
pipeline.reset()
def read(self, from_user, to_user, count=20, set_read=True):
key = self.chat_list_key(from_user, to_user)
data = self.redis.lrange(key, 0, count) or []
if set_read:
pipeline = self.redis.pipeline()
try:
pipeline.delete(key)
pipeline.execute()
finally:
pipeline.reset()
return data
很简单,简单就是美吗:
cimport functools
import hashlib
import logging
import struct
import time
import tornado.escape
import tornado.web
from tornado import httpserver
import tornado.websocket
import random
class PollingHandler(tornado.web.RequestHandler):
def post(self):
num = random.randint(1, 100)
self.write("welcome:E" + str(num))
self.finish()
class StreamingHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def post(self):
self.get_data(callback=self.on_finish)
def get_data(self, callback):
if self.request.connection.stream.closed():
return
num = random.randint(1, 100)
callback(num)
def on_finish(self, data):
self.write("Server says: %d" % data)
self.flush()
tornado.ioloop.IOLoop.instance().add_timeout(
time.time() + 3,
lambda: self.get_data(callback=self.on_finish)
)
class LongPollingHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def post(self):
self.get_data(callback=self.on_finish)
def get_data(self, callback):
if self.request.connection.stream.closed():
return
num = random.randint(1, 100)
tornado.ioloop.IOLoop.instance().add_timeout(
time.time() + 3,
lambda: callback(num)
)
def on_finish(self, data):
self.write("Server says: %d" % data)
self.finish()
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
num = random.randint(1, 100)
self.write_message("Server Say:"+str(num))
def on_message(self, message):
logging.info("getting message %s", message)
self.write_message("You say:" + message)
def on_close(self):
print "[WebSocket closed]......"
if __name__ == "__main__":
application = tornado.web.Application([
(r"/websocket", WebSocketHandler),
(r"/polling", PollingHandler),
(r"/streaming", StreamingHandler),
(r"/longpolling", LongPollingHandler),
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "/Users/liuzheng/py.work.dir/comet"}),
])
port = 8888
print "[start server on port:%s]......." % port
http_server = httpserver.HTTPServer(application)
http_server.bind(port)
http_server.start(2)
tornado.ioloop.IOLoop.instance().start()
WebSocket:未来方向
以上不管是Comet的何种方式,其实都只是单向通信,直到WebSocket的出现,才是B/S之间真正的全双工通信。不过目前WebSocket协议仍在开发中,目前Chrome和Safri浏览器默认支持WebSocket,而FF4和Opera出于安全考虑,默认关闭了WebSocket,IE则不支持(包括9),目前WebSocket协议最新的为“76号草案”。有兴趣可以关注http://dev.w3.org/html5/websockets/。