最后更新于 .

上一篇文章聊了下数据存储和常用的传输协议,不过对于自定义传输协议这里留了个坑,正好有点时间,就抓紧填上:)

既然选择原生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我都比较喜欢,所以我之前分别在其基础上封装了 tkolagkola,他们的底层都是依赖 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,先这样,下篇我们真正说协议定义的问题。

Pingbacks

Pingbacks已打开。

Trackbacks

引用地址

评论

  1. shasharui

    shasharui on #

    全栈程序员啊

    Reply

  2. z拽_

    z拽_ on #

    gevent 还不错~~~不过我用twisted~也还好~就是逻辑复杂时头疼。涉及到web时,也很头疼。。。可能主要还是在socket层用的多吧~

    Reply

    1. Dante

      Dante on #

      嗯,twisted一直想试一下,但是据说设计的很java,所以一直没动力抽出时间去看。。

      Reply

      1. z拽_

        z拽_ on #

        有点,体系比较复杂,很java~~~只是先接触了twisted而已,其实我也一直想深入下gevent~

        Reply

发表评论