读BitTorrent码日记5

# Written by Bram Cohen
# see LICENSE.txt for license information


#文件名称:RewServer.py
#读码日记:2004-9-7
#笔    者:zfive5(醉马不肖 之 [孤舟蓑笠翁, 独钓寒江雪])
#
#        分析RawServer类 主要负责socket通讯

#poll( [timeout])
#Polls the set of registered file descriptors, and returns a possibly-empty list containing (fd,
#event) 2-tuples for the descriptors that have events or errors to report. fd is the file #descriptor, and event is a bitmask with bits set for the reported events for that descriptor --
#POLLIN for waiting input, POLLOUT to indicate that the descriptor can be written to, and so #forth. An empty list indicates that the call timed out and no file descriptors had any events to
#report. If timeout is given, it specifies the length of time in milliseconds which the system will
#wait for events before returning. If timeout is omitted, negative, or None, the call will block
#until there is an event for this poll object.

from bisect import insort
import socket
from cStringIO import StringIO
from traceback import print_exc
from errno import EWOULDBLOCK, EINTR
try:
    from select import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP
    timemult = 1000
except ImportError:
    from selectpoll import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP
    timemult = 1
from threading import Thread, Event
from time import time, sleep
import sys
from random import randrange

all = POLLIN | POLLOUT


#socket类,对基础socket类进行封装,这也是通讯的基础
class SingleSocket:
    def __init__(self, raw_server, sock, handler):
        self.raw_server = raw_server
        self.socket = sock
        self.handler = handler
        self.buffer = []
        self.last_hit = time()
        self.fileno = sock.fileno()
        self.connected = False
    #得到连接对方的ip地址   
    def get_ip(self):
        try:
            return self.socket.getpeername()[0]
        except socket.error:
            return 'no connection'
       
    #关闭socket连接,这里有点像window的closesocket()函数
    def close(self):
        sock = self.socket
        self.socket = None
        self.buffer = []
        del self.raw_server.single_sockets[self.fileno]
        self.raw_server.poll.unregister(sock)
        sock.close()
   
    #这就不用说了,是shutdown()了
    def shutdown(self, val):
        self.socket.shutdown(val)

    #判断缓冲区列表是否为空
    def is_flushed(self):
        return len(self.buffer) == 0

    #功能就是把数据写到缓冲区列表中,当列表元素为1时,发送数据
    #给连接对方
    def write(self, s):
        assert self.socket is not None
        self.buffer.append(s)
        if len(self.buffer) == 1:
            self.try_write()
   
    #发送数据给对方
    def try_write(self):
        if self.connected:
            try:
                while self.buffer != []:
                    amount = self.socket.send(self.buffer[0])
                    if amount != len(self.buffer[0]):
                        if amount != 0:
                            self.buffer[0] = self.buffer[0][amount:]
                        break
                    del self.buffer[0]
            except socket.error, e:
                code, msg = e
                if code != EWOULDBLOCK:
                    self.raw_server.dead_from_write.append(self)
                    return
        #缓冲区为空,就只注册读数据事件
        if self.buffer == []:
            self.raw_server.poll.register(self.socket, POLLIN)
        else:
        #要不读写都注册
            self.raw_server.poll.register(self.socket, all)

#服务器类
class RawServer:
    def __init__(self, doneflag, timeout_check_interval, timeout, noisy = True, errorfunc = None):
        self.timeout_check_interval = timeout_check_interval
        self.timeout = timeout
        self.poll = poll()
        # {socket: SingleSocket}
        self.single_sockets = {}
        self.dead_from_write = []
        self.doneflag = doneflag
        self.noisy = noisy
        self.errorfunc = errorfunc
        self.funcs = []
        self.externally_added = []
        self.add_task(self.scan_for_timeouts, timeout_check_interval)

    #增加一个任务到任务列表,最后运行时刻为time()+delay
    def add_task(self, func, delay):
        insort(self.funcs, (time() + delay, func))

    #增加一个附加的任务到任务列表,最后运行时刻为time()+delay
    def external_add_task(self, func, delay = 0):
        self.externally_added.append((func, delay))

    #检查服务器超时socket函数,如果超时关掉处理函数
    def scan_for_timeouts(self):
        self.add_task(self.scan_for_timeouts, self.timeout_check_interval)
        t = time() - self.timeout
        tokill = []
        for s in self.single_sockets.values():
            if s.last_hit < t:
                tokill.append(s)
        for k in tokill:
            if k.socket is not None:
                self._close_socket(k)

    #捆绑一个端口,设置非阻塞方式,服务器socket与事件注册对应
    def bind(self, port, bind = '', reuse = False):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if reuse:
            server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.setblocking(0)
        server.bind((bind, port))
        server.listen(5)
        self.poll.register(server, POLLIN)
        self.server = server

    #连接对方,设置非阻塞方式,socket与事件注册对应
    def start_connection(self, dns, handler = None):
        if handler is None:
            handler = self.handler
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(0)
        try:
            sock.connect_ex(dns)
        except socket.error:
            raise
        except Exception, e:
            raise socket.error(str(e))
        self.poll.register(sock, POLLIN)
        s = SingleSocket(self, sock, handler)
        self.single_sockets[sock.fileno()] = s
        return s
       
    #处理服务器和所有与服务器连接的数据请求,events是socket和关心事件对应列表
    def handle_events(self, events):
        for sock, event in events:
            if sock == self.server.fileno():
                #错误关闭服务器
                if event & (POLLHUP | POLLERR) != 0:
                    self.poll.unregister(self.server)
                    self.server.close()
                    self.errorfunc('lost server socket')
                else:
                    #服务器accept
                    try:
                        newsock, addr = self.server.accept()
                        newsock.setblocking(0)
                        nss = SingleSocket(self, newsock, self.handler)
                        self.single_sockets[newsock.fileno()] = nss
                        #注册读数据事件
                        self.poll.register(newsock, POLLIN)
                        self.handler.external_connection_made(nss)
                    except socket.error:
                    #accept失败sleep(1),然后继续
                        sleep(1)
            else:
                s = self.single_sockets.get(sock)
                if s is None:
                    continue
                s.connected = True
                #错误关闭socket
                if (event & (POLLHUP | POLLERR)) != 0:
                    self._close_socket(s)
                    continue
  
                #读数据处理过程
                if (event & POLLIN) != 0:
                    try:
                        s.last_hit = time()
                        data = s.socket.recv(100000)
                        if data == '':
                            self._close_socket(s)
                        else:
                            #调用处理函数
                            s.handler.data_came_in(s, data)
                    except socket.error, e:
                        code, msg = e
                        if code != EWOULDBLOCK:
                            self._close_socket(s)
                            continue
                #写数据处理过程
                if (event & POLLOUT) != 0 and s.socket is not None and not s.is_flushed():
                    s.try_write()
                    if s.is_flushed():
                        s.handler.connection_flushed(s)
    #弹出附加任务
    def pop_external(self):
        try:
            while True:
                (a, b) = self.externally_added.pop()
                self.add_task(a, b)
        except IndexError:
            pass

    #download里调用的处理函数,这里主要是完成数据处理和调用处理
    def listen_forever(self, handler):
        self.handler = handler
        try:
            #如果完成标志被执就退出
            while not self.doneflag.isSet():
                try:
                    #加入一个附加任务
                    self.pop_external()
                   
                    #任务列表为空
                    if len(self.funcs) == 0:
                        period = 2 ** 30
                    else:
                    #任务列表不为空
                        period = self.funcs[0][0] - time()

                    间隔小于零,就间隔执零
                    if period < 0:
                        period = 0
                   
                    #设置注册事件超时
                    events = self.poll.poll(period * timemult)

                    #如果完成标志被执就退出
                    if self.doneflag.isSet():
                        return
                   
                    #调用任务处理函数
                    while len(self.funcs) > 0 and self.funcs[0][0] <= time():
                        garbage, func = self.funcs[0]
                        del self.funcs[0]
                        try:
                            func()
                        except KeyboardInterrupt:
                            print_exc()
                            return
                        except:
                            if self.noisy:
                                data = StringIO()
                                print_exc(file = data)
                                self.errorfunc(data.getvalue())

                    #关闭无反映的socket
                    self._close_dead()
                   
                    #处理socket请求或数据接收、发送
                    self.handle_events(events)

                    #如果完成标志被执就退出
                    if self.doneflag.isSet():
                        return

                    #关闭无反映的socket
                    self._close_dead()
                except error:
                    if self.doneflag.isSet():
                        return
                except KeyboardInterrupt:
                    print_exc()
                    return
                except:
                    data = StringIO()
                    print_exc(file = data)
                    self.errorfunc(data.getvalue())
        finally:
            for ss in self.single_sockets.values():
                ss.close()
            self.server.close()
   
    #关闭死的连接
    def _close_dead(self):
        while len(self.dead_from_write) > 0:
            old = self.dead_from_write
            self.dead_from_write = []
            for s in old:
                if s.socket is not None:
                    self._close_socket(s)
    #关闭socket连接
    def _close_socket(self, s):
        sock = s.socket.fileno()
        s.socket.close()
        self.poll.unregister(sock)
        del self.single_sockets[sock]
        s.socket = None
        s.handler.connection_lost(s)

#下面是测试用例,这里省略不写,接下来Storage类留到下次在分析吧。。。。。

你可能感兴趣的:(读BitTorrent码日记5)