大家好,下面我和大家分享一下非诚勿扰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/。
作为一个Objective-C的coder,我总能听到一部分人在这门语言上抱怨有很多问题。他们总在想快速学习这门语言来写一个App出来,但他们也总是联想到Objective-C看上去实在太难了或者在想这些语法符号都是神马玩意?不错,他们问得非常好,所以本人也解释一下为什么很多程序员相比较学习Ruby或者Java很容易,但在决定开发iOS或者OS X应用时会那么犹豫。
首先我们谈谈神马叫做编程语言,编程语言是一种让人们能读懂并且能够展现程序的执行行为的语言,包括语法(正确的表达式以及状态机的使用规则)以及语义(如何去组织这些表达式以及状态机以一种有意义的方式去完成我们的目标)
大部分编程语言都是基于C语法风格的,也就是我们常说的点语法,依靠小数点符号来调用方法或者获取类的成员(当然了,在C语言中类被替代为结构体)。它们通常都是自己实现类的定义(注意在这里Java与C#和 C++有稍微一点不一样),但无论如何这些语言的具体细节都是一样的。当我们接触一个新的编程语言时,我们当然很乐意看到新语言的语法是我们似曾相识的。毕竟大部分程序员都了解C语言的语法。
然而Objective-C压根就不是C语法风格的编程语言。它与我们熟悉的语言语法区别太大以至于大部分程序员根本看不懂这些Objective-C代码是神马东西。
甚至其他一些像Python或者Ruby这样的“奇怪”的语言都能让人看个大概出来,毕竟Python与Ruby至少还是点语法风格的,使用点语法来调用方法以及获取类的成员。相反它们缺乏分支结构并不会长时间影响程序员的理解,毕竟这两种语言也有自己的方式来完成同样的目标。
下面我们来介绍一下Objective-C语言: 这里我会拿另一个OOP语言来与它进行比较
我想这里大家肯定还不会有什么问题,来看看这个:
好了好了,我想肯定有部分人想问一大堆问题了。但这还不是最糟糕的,我们接着来看一个更强悍的:
一般到了这里,我想大家都头都大了几圈。
这到底是个神马情形?这么多分号!太恐怖了!接着会冒出一大堆问题:这到底调用的是神马方法?又是怎么调用的?是performAction吗还是别的东西?方法重载该如何做?哦,我的天!
最好先忘了我们熟悉的方法调用。实际上,当你看到performAction:withTwoParameters:时,我们其实在谈论消息而不是方法。当你能这么理解时问题就好办多了。
实际上Objective-C是很严格的C语言的超集。而与流行的编程语言风格又差距太大。作为C语言的超集,它增加了很多东西,我们将简单看看它到底增加了什么复杂的东西。
Objective-C是一个很“大”的语言,我是说它的语法很复杂而且又具备弹性。Apple公司已经尽可能减小了Objective-C语言的体积,然而,他们还是添加了一些东西来让Objecive-C表达式更加简单。
- Synthesizing properties
- Dot-syntax for accessing getters/setters
- Garbage Collection
- Blocks (closures)
- Automatic Reference Counting
- Weak references
所以虽然这个语言越来越大了,但是在表达同样的目的使用的代码却越来越简单。
运行时:
Objective-C的运行时是动态的,它能让你在运行时为类添加方法或者去除方法以及使用反射。
除非你用过像Ruby或者Lisp这类支持这种特性的语言,否则你会感觉很怪异。但请放心!在你尝试使用一些好的新特性时一开始都是很郁闷的。
框架:
如果离开了Cocoa/Cocoa Touch框架,那么Objective-C简直没法创建App。就如同Ruby离开了Rails就没法创建Web应用程序一样。
Cocoa既大又老。比如看看UIView的contentStretch属性,整个头文件就这么点东西:
就这么多?首先我假定它定义了一个可拉伸的内容区域,但是这是如何做到的?矩形单元又在哪里?Cocoa/Cocoa-Touch是解释这个的唯一途径,作为Objective-C的新人,你可以去咨询它。
历史:
纵向看来,Apple公司领导着Objective-C语言的发展与维护,包括Objective-C运行时,Cocoa/Cocoa-Touch框架以及Objective-C语言的编译器。看上去Objective-C就如同以下一个云状图:
当你去学习Objective-C时,它不仅仅就是一个框架,一个运行时或者一个编译器;而是它们全部。还有个很重要的部件就是 Automatic Reference Counting(使用后无法直接调用dealloc方法),这玩意会让你使用Cocoa/Cocoa-Touch编程时更加方便。
紧密耦合系统代码设计是Objective-C独有的。其他语言比如在.Net框架下运行的Iron Python。以及一大堆运行在JVM的非Java语言,比如Clojure。甚至Ruby以及Rails都是两个不同的项目。当你打算使用其他语言配合 Cocoa/Cocoa-Touch以及Objective-C运行时时。你唯一的选择就是Mac Ruby。
未来 :
这4年来,我们可以看到Apple公司已经尽可能的减小了Objective-C语言的大小以及复杂度。他们也完成了手动内存自动管理以及经典C头文件的方式;而且开始慢慢对属性存取器引入点语法以及 Automatic Reference Counting来方便程序员编程。但就现在看来,Objective-C还是比较难接触与学习的;之所以现在变得越来越流行唯一的原因就是它是做iOS app的唯一途径了。
在上一文 给出了 textview多行设置ellipsize="end"并不显示省略号的解决方法(当然如果不是中文 android-textview-multiline-ellipse 也是个)。上一文中通过measureText计算出最后一行的文字是否超出了显示范围,由于文字的宽度不是均匀的,所以这个方法在某些时候就会出现多一两个字或少一两个字。
下面给出第二中解决方法的主要代码:
protected void onDraw(Canvas canvas) { if (HAS_BUG && !mChecked) { mChecked = true; Layout layout = super.getLayout(); int maxLinex = mMaxLines > 0 ? mMaxLines : 1; if (layout.getLineCount() > maxLinex) { if (mELLIPSEWidth == 0) { mELLIPSEWidth = (int) getPaint().measureText(ELLIPSE_END); } mOriText = super.getText(); int width = layout.getWidth(), llw = (int) layout.getLineWidth(maxLinex-1), lci = layout.getLineEnd(maxLinex-1); if (mELLIPSEWidth + llw > width) { int spc = mELLIPSEWidth + llw - width; int w = (int) (spc / super.getTextSize()); if (spc % super.getTextSize() != 0) { ++w; } lci -= w; } super.setText(mOriText.subSequence(0, lci) + ELLIPSE_END); } } super.onDraw(canvas); }
下面是显示效果
附该控件源码。需要示例的请移步 textviewellipseendfixed