上一篇文章聊了下数据存储和常用的传输协议,不过对于自定义传输协议这里留了个坑,正好有点时间,就抓紧填上:)
既然选择原生socket,那么有个基本的选择就是tcp/udp的问题.
这个其实还是看业务自己的选择,只是如果选择了udp的话,那么很多问题都可以不用考虑,比如粘包问题。但是udp有个限制是每次传输的数据大小不能超过64K,这个要注意。
为了考虑复杂的情况,我们还是主要说tcp的实现,这篇文章先说下socket使用相关的一些库和代码吧
Android端开发
对于android端,我们有两个主要选择:阻塞socket和非阻塞socket。
阻塞socket就是正常的socket,当调用recv的时候,会阻塞住直到返回数据。
非阻塞socket在android上可以直接使用nio,因为自己之前一直是做c++和python,所以其实一开始nio的时候真心有些不太使用,把几个要注意的点列一下:
1. 当网络断线的时候,有些手机如S4,会出现这个问题,channel.finishConnect()会一直等超时 60秒。之后会抛出一个 TimeoutException.
解决方法是:
channel.socket().setSoTimeout(5)
来设置成只等5秒。
比较诡异的是,明明是异步io了,为什么还是会有等待超时的情况。还要在研究下。
2. 当网络不在线的时候,启动connect,依然可以进入isConnectable的判断中,并且可以调用finishconnect。
解决方案:finishConnect之后,调用write一个空字符串进去,如果抛出异常就证明没有链接成功
另外,与linux原生的非阻塞socket的recv不同,nio下channel的 read 结果及意义如下
-1: socket被关闭,关闭channel,并取消key
0: 没有数据可读,直接返回即可
大于0: 读取到的数据长度
Server端开发
server端的选择就更加丰富一些了,也是分阻塞和非阻塞socket来说吧
阻塞socket
阻塞socket的最简单即标准库自带的 ThreadingTCPServer,这个会在每个请求来的时候启动一个线程。这种server与预分配进程等方法其实是一个性质,性能是比较大的问题
非阻塞socket
非阻塞socket的选择也有不少,比如python标准库里的asyncore和asynchat,但是这真的是个很老的库了,只支持select和poll,连epoll都支持不了。但是使用是没有任何问题的
而作为asyncore的一个替代,tornado提供了一个更好的选择。当然很多人知道tornado是因为他是一个高性能的http server,但是其实他底层封装的ioloop那套异步io库也是可以单独使用的。
但是基于异步io的server用起来其实是比较痛苦的,因为所有的业务操作都不能阻塞。当然,可以使用类似celery之类的任务分派工具,但是多一层进程通信会导致性能更加下降。
还好python世界还有另一个选择:gevent,基于gevent写的tcp server在兼顾了性能的同时,大部分的阻塞请求代码都可以直接使用。
tornado 和 gevent实现的server我都比较喜欢,所以我之前分别在其基础上封装了 tkola 和 gkola,他们的底层都是依赖 kola的。
之所以要做这样的封装,是因为自己对flask @route 式的写法情有独钟,而这样的封装就是实现了这种风格的编码,当然还有一些更有意思的东西在里面。
gkola的示例代码如下:
from gkola import Kola, logger app = Kola() @app.create_conn def create_conn(conn): logger.error('create_conn') @app.close_conn def close_conn(conn): logger.error('close_conn') @app.before_first_request def before_first_request(request): logger.error('before_first_request') @app.before_request def before_request(request): logger.error('before_request') @app.after_request def after_request(request, exc): logger.error('after_request') @app.before_response def before_response(conn, rsp): logger.error('rsp: %s', rsp) @app.repeat_timer(5) def repeat_timer(): logger.error('repeat_timer') @app.route() def index(request): request.write(dict( ret=0,index=True )) app.run('127.0.0.1', 5500)ok,先这样,下篇我们真正说协议定义的问题。
shasharui on #
全栈程序员啊
Reply
z拽_ on #
gevent 还不错~~~不过我用twisted~也还好~就是逻辑复杂时头疼。涉及到web时,也很头疼。。。可能主要还是在socket层用的多吧~
Reply
Dante on #
嗯,twisted一直想试一下,但是据说设计的很java,所以一直没动力抽出时间去看。。
Reply
z拽_ on #
有点,体系比较复杂,很java~~~只是先接触了twisted而已,其实我也一直想深入下gevent~
Reply