zoukankan      html  css  js  c++  java
  • BT源代码学习心得(五):统一网络服务接口RawServer

    BT源代码学习心得(五):统一网络服务接口--RawServer

    发信人: wolfenstein (NeverSayNever), 个人文集
    标  题: BT源代码学习心得(五):统一网络服务接口--RawServer
    发信站: 水木社区 (Fri Aug  5 18:54:08 2005), 文集
    (本文包含HTML标记,终端模式下可能无法正确浏览)
        以后的部分都需要网络服务(种子文件的生成在本地就可以完成,但是通过这些种子文
    件下载实际的内容和提供跟踪器服务都需要网络),在BT的程序设计中,为网络服务提供了
    统一的接口,这样程序中的其它部分需要打开一个网络服务时,只需要向这个接口进行注册
    ,并提供相应的处理对象(handler)即可,当网络事件发生时,将会自动这个处理对象中的
    相关函数进行处理。
        这个统一网络服务接口定义在BitTorrent/RawServer.py中,由它去实际调用和网络插
    口(socket)有关的库,另外,RawServer还提供add_task功能,可以允许一些任务被延后执
    行。
        RawServer在初始化的时候,可以从外部传入一个doneflag参数,这是一个Event的数据
    类型,可以从其它地方触发它,这样可以随时中断RawServer中的主循环(listen_forever中
    的)。另外还进行一些内部变量的设置。最后,它给自己增加了一个任务,
    scan_for_timeouts,这个任务会定时得检查超时的网络连接,并关闭它们。
        我们可以看到add_task的所做的工作就是将要延时执行的任务计算出它的实际执行时间
    ,并把它添加到一个排好序的列表中(funcs),且保持这个列表仍然处于有序状态,这个列
    表以实际执行时间为顺序。
        当其它模块要提供网络服务时,它首先调用RawServer的create_serversocket函数,这
    个函数会返回一个socket对象,并且这个socket返回时,已经处于listen状态了。当然,这
    个时候如果真有外部的网络连接进来,还是不会有什么动作的,因为相应的处理对象还没有
    注册进来。
        接下来应该调用start_listening函数,这个函数的作用是把得到的网络插口和它对应
    的处理对象添加到一个字典中,该字典以网络插口的描述符(FD)为主键。值得注意的是,这
    个函数名称中虽然有listen字样,但是socket.listen函数却不是在这里调用,而是在
    create_serversocket就已经被调用了。传递进来的处理对象的类型没有限制,唯一的要求
    是它必须包含有external_connection_made函数,这样当外部网络连接到来时,这个函数就
    会被调用。处理对象通常还应提供data_came_in函数来处理网络数据,以及
    connection_flushed函数来处理数据已经正式发出(相对于还在缓冲区的情况)时的处理,后
    面两个函数也可以不提供,因为在external_connection_made函数里,可以把新连接的网络
    数据处理对象重新定位到一个包含有data_came_in函数和connection_flushed函数的对象。
    start_listening函数处理完后,该网络插口就已经存在于serversockets字典中了。
        而当其它模块要连接到外部网络时,应该调用start_connection函数,这个函数将把网
    络插口添加到另一个字典single_sockets中,当然,使用了SingleSocket对象对其进行了一
    定程度的包装。从后面的分析可以看到,这个SingleSocket对象的主要功能是对输出的数据
    进行了一定的缓冲,并在不会阻塞的情况下把这些数据实际得写到socket中。
    start_connection需要传入的处理对象是必须包含data_came_in而可以不包含
    external_connection_made的对象。
        在start_listening和start_connection中都用到了poll对象,这是系统提供的一个提
    供轮询机制的模块,使用文件描述符作为参数,可以得到相应的事件(即该文件描述符对应
    的插口有数据流入或者留出等),而在这两个函数中,都调用了poll的注册函数,方便后面
    的poll轮询操作。
        需要注意的是,在上面的这些函数被执行后,网络连接还是不会被处理,因为虽然打开
    了相应的网络插口,也注册了相应的处理对象,但是整个的轮询机制还没有建立起来。直到
    listen_forever函数被调用后,这个机制才真正得建立起来。这个函数的主体就是一个无限
    的while循环,只有doneflag这个事件可以被用来中止这个循环。它首先做的事情是从添加
    的任务funcs寻找最近要执行的任务的时间,并与当前时间相减,计算出period,然后用
    poll轮询这么长的时间,这样做就可以保证轮询结束后不会耽误外部任务过久。轮询到的结
    果返回在events里,这是一个列表,它的元素是以文件描述符和事件所组成的二元组。接下
    来就是根据时间的情况,把需要马上执行的外部任务都执行了,_make_wrapped_call的主要
    作用就是执行外部任务,只是给它们增加一些意外处理的保护代码。执行完这些外部任务后
    ,调用_close_dead关闭不活跃的网络连接,接下来就是使用_handle_events来处理前面的
    poll搜集到的网络事件了。
        _handle_events的主体是一个for循环,检查每一个sock和它对应的event。首先看它是
    在serversockets字典中还是在single_sockets字典中,如果是前者,那么这是一个侦听中
    的插口,再检查网络事件,如果不是出错事件的话,那么就说明是有外部连接到达,熟悉
    socket编程的人都应该知道,这时正确的处理方式是建立一个新的socket,然后让侦听中的
    插口去accept它,以后数据的读写应该在新的socket中进行。接下来的处理也是这样,新的
    socket被用SingleSocket包装起来了,并且也被放到single_sockets字典中,因为它和用
    start_connection建立的socket一样,都是有可能有数据流入的,而侦听的插口只需要处理
    网络连接。接下来,前面注册的处理对象中的external_connection_made函数被调用了,允
    许进行一些其它的相关操作,我们注意到,这里处理对象被原封不动得传入到新的
    SingleSocket中,当然实际上在external_connection_made函数中可以把SingleSocket的处
    理对象重定向到其它对象中。
        接下来的else语句说明sock在single_sockets字典中,只有一种情况例外,就是
    os.pipe。这种情况下不用处理这个事件,直接continue处理下一个事件即可。然后检查事
    件,如果是出错则关闭该插口,否则就说明是有数据流动,而数据流动无非是流入和流出两
    种情况,如果是流入的话,就把数据读到一个缓冲区里,然后调用处理对象中提供的
    data_came_in进行处理,而data_came_in得到的参数直接就是缓冲区中的数据,它不需要再
    处理socket以及考虑可能会形成的阻塞等问题了。另外由于SingleSocket中对写操作也进行
    了包装,即如果网络有阻塞的可能,数据也会先写入缓冲区,这样data_came_in中就可以随
    便调用s.write了。最后如果是数据流出,则调用s.try_write,这个函数实现得也很安全。
    最后检查是否数据都已经真的发出去了(flushed),如果是,则调用处理对象中提供的
    connection_flushed函数进行收尾工作。
        以后我们可以看到,在BT的实现中,创建了各种各样的对象,而且这些对象之间有各种
    各样比较复杂的关系,但是所有的网络服务,都是通过RawServer来进行的,再具体一些,
    那就是RawServer这个对象只会被创建一个,而所有要求网络服务的模块都会把网络服务的
    处理对象注册到这个RawServer中,方便统一管理。
        最后说一下,今天用google搜索发现原来去年就已经有人分析过BT的源代码,不仅感叹
    自己孤陋寡闻,不过发现现在的版本(4.0.3)和当时的版本已经有了一些差别,而且我也可
    以以我的阅读源代码的思路继续前进,提供给大家一个不同的视角,因而决定把我的学习心
    得继续写完,希望大家能够支持。
  • 相关阅读:
    hdu 2647 Reward
    hdu 2094 产生冠军
    hdu 3342 Legal or Not
    hdu 1285 确定比赛名次
    hdu 3006 The Number of set
    hdu 1429 胜利大逃亡(续)
    UVA 146 ID Codes
    UVA 131 The Psychic Poker Player
    洛谷 P2491消防 解题报告
    洛谷 P2587 [ZJOI2008]泡泡堂 解题报告
  • 原文地址:https://www.cnblogs.com/kokoliu/p/616907.html
Copyright © 2011-2022 走看看