예제 #1
0
class Upload:
    def __init__(self, connection, choker, storage, max_slice_length, max_rate_period, fudge):
        self.connection = connection
        self.choker = choker
        self.storage = storage
        self.max_slice_length = max_slice_length
        self.max_rate_period = max_rate_period
        self.choked = True
        self.interested = False
        self.buffer = []
        self.measure = Measure(max_rate_period, fudge)
        if storage.do_I_have_anything():
            connection.send_bitfield(storage.get_have_list())

    def send_have(self):
        if self.storage.get_amount_left() == 0 and self.supseed.Enabled():
            want = self.connection.download._want
            piece = self.supseed.Next(want)
            if piece != None:
                self.connection.send_have(piece)

    def got_have(self, index):
        self.send_have()

    def got_not_interested(self):
        if self.interested:
            self.interested = False
            del self.buffer[:]
            self.choker.not_interested(self.connection)

    def got_interested(self):
        if not self.interested:
            self.interested = True
            self.choker.interested(self.connection)

    def flushed(self):
        while len(self.buffer) > 0 and self.connection.is_flushed():
            index, begin, length = self.buffer[0]
            del self.buffer[0]
            piece = self.storage.get_piece(index, begin, length)
            if piece is None:
                self.connection.close()
                return
            self.measure.update_rate(len(piece))
            self.connection.send_piece(index, begin, piece)

    def got_request(self, index, begin, length):
        if not self.interested or length > self.max_slice_length:
            self.connection.close()
            return
        if not self.choked:
            self.buffer.append((index, begin, length))
            self.flushed()

    def got_cancel(self, index, begin, length):
        try:
            self.buffer.remove((index, begin, length))
        except ValueError:
            pass

    def choke(self):
        if not self.choked:
            self.choked = True
            del self.buffer[:]
            self.connection.send_choke()

    def unchoke(self):
        if self.choked:
            self.choked = False
            self.connection.send_unchoke()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def has_queries(self):
        return len(self.buffer) > 0

    def get_rate(self):
        return self.measure.get_rate()
예제 #2
0
class SingleDownload:
    def __init__(self, downloader, connection):
        self.downloader = downloader
        self.connection = connection
        self.choked = True
        self.interested = False
        self.active_requests = []
        self.measure = Measure(downloader.max_rate_period)
        self.have = Bitfield(downloader.numpieces)
        self.last = 0
        self.example_interest = None

    def disconnected(self):
        self.downloader.downloads.remove(self)
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.lost_have(i)
        self._letgo()

    def _letgo(self):
        if not self.active_requests:
            return
        if self.downloader.storage.is_endgame():
            self.active_requests = []
            return
        lost = []
        for index, begin, length in self.active_requests:
            self.downloader.storage.request_lost(index, begin, length)
            if index not in lost:
                lost.append(index)
        self.active_requests = []
        ds = [d for d in self.downloader.downloads if not d.choked]
        shuffle(ds)
        for d in ds:
            d._request_more(lost)
        for d in self.downloader.downloads:
            if d.choked and not d.interested:
                for l in lost:
                    if d.have[l] and self.downloader.storage.do_I_have_requests(
                            l):
                        d.interested = True
                        d.connection.send_interested()
                        break

    def got_choke(self):
        if not self.choked:
            self.choked = True
            self._letgo()

    def got_unchoke(self):
        if self.choked:
            self.choked = False
            if self.interested:
                self._request_more()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def got_piece(self, index, begin, piece):
        try:
            self.active_requests.remove((index, begin, len(piece)))
        except ValueError:
            return False
        if self.downloader.storage.is_endgame():
            self.downloader.all_requests.remove((index, begin, len(piece)))
        self.last = time()
        self.measure.update_rate(len(piece))
        self.downloader.measurefunc(len(piece))
        self.downloader.downmeasure.update_rate(len(piece))
        if not self.downloader.storage.piece_came_in(index, begin, piece):
            if self.downloader.storage.is_endgame():
                while self.downloader.storage.do_I_have_requests(index):
                    nb, nl = self.downloader.storage.new_request(index)
                    self.downloader.all_requests.append((index, nb, nl))
                for d in self.downloader.downloads:
                    d.fix_download_endgame()
                return False
            self.downloader.picker.bump(index)
            ds = [d for d in self.downloader.downloads if not d.choked]
            shuffle(ds)
            for d in ds:
                d._request_more([index])
            return False
        if self.downloader.storage.do_I_have(index):
            self.downloader.picker.complete(index)
        if self.downloader.storage.is_endgame():
            for d in self.downloader.downloads:
                if d is not self and d.interested:
                    if d.choked:
                        d.fix_download_endgame()
                    else:
                        try:
                            d.active_requests.remove(
                                (index, begin, len(piece)))
                        except ValueError:
                            continue
                        d.connection.send_cancel(index, begin, len(piece))
                        d.fix_download_endgame()
        self._request_more()
        if self.downloader.picker.am_I_complete():
            for d in [
                    i for i in self.downloader.downloads
                    if i.have.numfalse == 0
            ]:
                d.connection.close()
        return self.downloader.storage.do_I_have(index)

    def _want(self, index):
        return self.have[index] and self.downloader.storage.do_I_have_requests(
            index)

    def _request_more(self, indices=None):
        assert not self.choked
        if len(self.active_requests) == self.downloader.backlog:
            return
        if self.downloader.storage.is_endgame():
            self.fix_download_endgame()
            return
        lost_interests = []
        while len(self.active_requests) < self.downloader.backlog:
            if indices is None:
                interest = self.downloader.picker.next(self._want,
                                                       self.have.numfalse == 0)
            else:
                interest = None
                for i in indices:
                    if self.have[
                            i] and self.downloader.storage.do_I_have_requests(
                                i):
                        interest = i
                        break
            if interest is None:
                break
            if not self.interested:
                self.interested = True
                self.connection.send_interested()
            self.example_interest = interest
            begin, length = self.downloader.storage.new_request(interest)
            self.downloader.picker.requested(interest, self.have.numfalse == 0)
            self.active_requests.append((interest, begin, length))
            self.connection.send_request(interest, begin, length)
            if not self.downloader.storage.do_I_have_requests(interest):
                lost_interests.append(interest)
        if not self.active_requests and self.interested:
            self.interested = False
            self.connection.send_not_interested()
        if lost_interests:
            for d in self.downloader.downloads:
                if d.active_requests or not d.interested:
                    continue
                if d.example_interest is not None and self.downloader.storage.do_I_have_requests(
                        d.example_interest):
                    continue
                for lost in lost_interests:
                    if d.have[lost]:
                        break
                else:
                    continue
                interest = self.downloader.picker.next(d._want,
                                                       d.have.numfalse == 0)
                if interest is None:
                    d.interested = False
                    d.connection.send_not_interested()
                else:
                    d.example_interest = interest
        if self.downloader.storage.is_endgame():
            self.downloader.all_requests = []
            for d in self.downloader.downloads:
                self.downloader.all_requests.extend(d.active_requests)
            for d in self.downloader.downloads:
                d.fix_download_endgame()

    def fix_download_endgame(self):
        want = [
            a for a in self.downloader.all_requests
            if self.have[a[0]] and a not in self.active_requests
        ]
        if self.interested and not self.active_requests and not want:
            self.interested = False
            self.connection.send_not_interested()
            return
        if not self.interested and want:
            self.interested = True
            self.connection.send_interested()
        if self.choked:
            return
        shuffle(want)
        del want[self.downloader.backlog - len(self.active_requests):]
        self.active_requests.extend(want)
        for piece, begin, length in want:
            self.connection.send_request(piece, begin, length)

    def got_have(self, index):
        if self.have[index]:
            return
        self.have[index] = True
        self.downloader.picker.got_have(index)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            self.fix_download_endgame()
        elif self.downloader.storage.do_I_have_requests(index):
            if not self.choked:
                self._request_more([index])
            else:
                if not self.interested:
                    self.interested = True
                    self.connection.send_interested()

    def got_have_bitfield(self, have):
        self.have = have
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.got_have(i)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            for piece, begin, length in self.downloader.all_requests:
                if self.have[piece]:
                    self.interested = True
                    self.connection.send_interested()
                    return
        for i in xrange(len(self.have)):
            if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                self.interested = True
                self.connection.send_interested()
                return

    def get_rate(self):
        return self.measure.get_rate()

    def is_snubbed(self):
        return time() - self.last > self.downloader.snub_time
예제 #3
0
class RateLimiter:
    def __init__(self, sched, unitsize, slotsfunc=lambda x: None):
        self.sched = sched
        self.last = None
        self.unitsize = unitsize
        self.slotsfunc = slotsfunc
        self.measure = Measure(MAX_RATE_PERIOD)
        self.autoadjust = False
        self.upload_rate = MAX_RATE * 1000
        self.slots = SLOTS_STARTING    # garbage if not automatic

    def set_upload_rate(self, rate):
        # rate = -1 # test automatic
        if rate < 0:
            if self.autoadjust:
                return
            self.autoadjust = True
            self.autoadjustup = 0
            self.pings = []
            rate = MAX_RATE
            self.slots = SLOTS_STARTING
            self.slotsfunc(self.slots)
        else:
            self.autoadjust = False
        if not rate:
            rate = MAX_RATE
        self.upload_rate = rate * 1000
        self.lasttime = clock()
        self.bytes_sent = 0

    def queue(self, conn):
        assert conn.next_upload is None
        if self.last is None:
            self.last = conn
            conn.next_upload = conn
            self.try_send(True)
        else:
            conn.next_upload = self.last.next_upload
            self.last.next_upload = conn
            self.last = conn

    def try_send(self, check_time=False):
        t = clock()
        self.bytes_sent -= (t - self.lasttime) * self.upload_rate
        self.lasttime = t
        if check_time:
            self.bytes_sent = max(self.bytes_sent, 0)
        cur = self.last.next_upload
        while self.bytes_sent <= 0:
            bytes = cur.send_partial(self.unitsize)
            self.bytes_sent += bytes
            self.measure.update_rate(bytes)
            if bytes == 0 or cur.backlogged():
                if self.last is cur:
                    self.last = None
                    cur.next_upload = None
                    break
                else:
                    self.last.next_upload = cur.next_upload
                    cur.next_upload = None
                    cur = self.last.next_upload
            else:
                self.last = cur
                cur = cur.next_upload
        else:
            self.sched(self.try_send, self.bytes_sent / self.upload_rate)

    def adjust_sent(self, bytes):
        self.bytes_sent = min(self.bytes_sent + bytes, self.upload_rate * 3)
        self.measure.update_rate(bytes)

    def ping(self, delay):
        if DEBUG:
            print delay
        if not self.autoadjust:
            return
        self.pings.append(delay > PING_BOUNDARY)
        if len(self.pings) < PING_SAMPLES + PING_DISCARDS:
            return
        if DEBUG:
            print 'cycle'
        pings = sum(self.pings[PING_DISCARDS:])
        del self.pings[:]
        if pings >= PING_THRESHHOLD:   # assume flooded
            if self.upload_rate == MAX_RATE:
                self.upload_rate = self.measure.get_rate() * ADJUST_DOWN
            else:
                self.upload_rate = min(self.upload_rate,
                                       self.measure.get_rate() * 1.1)
            self.upload_rate = max(int(self.upload_rate * ADJUST_DOWN), 2)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print 'adjust down to ' + str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_FIRST
        else:   # not flooded
            if self.upload_rate == MAX_RATE:
                return
            self.autoadjustup -= 1
            if self.autoadjustup:
                return
            self.upload_rate = int(self.upload_rate * ADJUST_UP)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print 'adjust up to ' + str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_NEXT
예제 #4
0
class Upload:
    def __init__(self, connection, choker, storage, max_slice_length,
                 max_rate_period, fudge):
        self.connection = connection
        self.choker = choker
        self.storage = storage
        self.max_slice_length = max_slice_length
        self.max_rate_period = max_rate_period
        self.choked = True
        self.interested = False
        self.buffer = []
        self.measure = Measure(max_rate_period, fudge)
        if storage.do_I_have_anything():
            connection.send_bitfield(storage.get_have_list())

    def got_not_interested(self):
        if self.interested:
            self.interested = False
            del self.buffer[:]
            self.choker.not_interested(self.connection)

    def got_interested(self):
        if not self.interested:
            self.interested = True
            self.choker.interested(self.connection)

    def flushed(self):
        while len(self.buffer) > 0 and self.connection.is_flushed():
            index, begin, length = self.buffer[0]
            del self.buffer[0]
            piece = self.storage.get_piece(index, begin, length)
            if piece is None:
                self.connection.close()
                return
            self.measure.update_rate(len(piece))
            self.connection.send_piece(index, begin, piece)

    def got_request(self, index, begin, length):
        if not self.interested or length > self.max_slice_length:
            self.connection.close()
            return
        if not self.choked:
            self.buffer.append((index, begin, length))
            self.flushed()

    def got_cancel(self, index, begin, length):
        try:
            self.buffer.remove((index, begin, length))
        except ValueError:
            pass

    def choke(self):
        if not self.choked:
            self.choked = True
            del self.buffer[:]
            self.connection.send_choke()

    def unchoke(self):
        if self.choked:
            self.choked = False
            self.connection.send_unchoke()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def has_queries(self):
        return len(self.buffer) > 0

    def get_rate(self):
        return self.measure.get_rate()
예제 #5
0
파일: Uploader.py 프로젝트: myliang/works
class Upload:
    def __init__(self, connection, choker, storage, 
            max_slice_length, max_rate_period, fudge):

        # sock connection
        self.connection = connection
        self.choker = choker
        self.storage = storage
        # 最大slice
        self.max_slice_length = max_slice_length
        # 最大速率间距
        self.max_rate_period = max_rate_period

        # local choke remote
        self.choked = True
        # remote not interested  local
        self.interested = False

        # 缓存
        self.buffer = []

        # 测速对象
        self.measure = Measure(max_rate_period, fudge)

        # 如果存储了数据,那么发送bitfield
        if storage.do_I_have_anything():
            connection.send_bitfield(storage.get_have_list())

    # 处理收到的不感兴趣消息
    def got_not_interested(self):
        if self.interested:
            self.interested = False
            # 删除缓存数据
            del self.buffer[:]

            # remote 对Local不感兴趣,重新rechoke
            self.choker.not_interested(self.connection)

    # 处理收到的感兴趣的消息
    def got_interested(self):
        if not self.interested:
            self.interested = True
            # remote is interested local, and rechoke
            self.choker.interested(self.connection)


    # flushed buffer and send message
    def flushed(self):
        # if the buffer len great zero and connection buffer is zero
        while len(self.buffer) > 0 and self.connection.is_flushed():
            index, begin, length = self.buffer[0]
            del self.buffer[0]
            # read piece from disk
            piece = self.storage.get_piece(index, begin, length)
            # if piece is not readed, so close connection
            if piece is None:
                self.connection.close()
                return
            # updat rate
            self.measure.update_rate(len(piece))
            # send piece
            self.connection.send_piece(index, begin, piece)

    # 处理收到的请求消息
    def got_request(self, index, begin, length):
        #如果remote is not interested local or request len gt max slice length (16K)
        # so close conn
        if not self.interested or length > self.max_slice_length:
            self.connection.close()
            return

        # if local choked remote
        # add request to buffer, and flushed buffer
        if not self.choked:
            self.buffer.append((index, begin, length))
            self.flushed()

    # got cancel message received
    def got_cancel(self, index, begin, length):
        try:
            # remove request message from buffer
            self.buffer.remove((index, begin, length))
        except ValueError:
            pass

    # send choke
    def choke(self):
        # if local unchoked remote
        # and choke it
        if not self.choked:
            self.choked = True
            # clean buffer
            del self.buffer[:]
            # send choke to remoate
            self.connection.send_choke()

    # send unchoke
    def unchoke(self):
        if self.choked:
            self.choked = False
            self.connection.send_unchoke()

    # local is choked remote
    def is_choked(self):
        return self.choked

    # remote is interested local
    def is_interested(self):
        return self.interested

    # has data or not in buffer
    def has_queries(self):
        return len(self.buffer) > 0

    # get update rate
    def get_rate(self):
        return self.measure.get_rate()
예제 #6
0
class RateLimiter:
    def __init__(self, sched, unitsize, slotsfunc=lambda x: None):
        self.sched = sched
        self.last = None
        self.unitsize = unitsize
        self.slotsfunc = slotsfunc
        self.measure = Measure(MAX_RATE_PERIOD)
        self.autoadjust = False
        self.upload_rate = MAX_RATE * 1000
        self.slots = SLOTS_STARTING

    def set_upload_rate(self, rate):
        if DEBUG:
            print >> sys.stderr, 'RateLimiter: set_upload_rate', rate
        if rate < 0:
            if self.autoadjust:
                return
            self.autoadjust = True
            self.autoadjustup = 0
            self.pings = []
            rate = MAX_RATE
            self.slots = SLOTS_STARTING
            self.slotsfunc(self.slots)
        else:
            self.autoadjust = False
        if not rate:
            rate = MAX_RATE
        self.upload_rate = rate * 1000
        self.lasttime = clock()
        self.bytes_sent = 0

    def queue(self, conn):
        if DEBUG:
            print >> sys.stderr, 'RateLimiter: queue', conn
        if self.last is None:
            self.last = conn
            conn.next_upload = conn
            self.try_send(True)
        else:
            conn.next_upload = self.last.next_upload
            self.last.next_upload = conn
            if not conn.connection.is_coordinator_con():
                self.last = conn

    def try_send(self, check_time=False):
        if DEBUG:
            print >> sys.stderr, 'RateLimiter: try_send'
        t = clock()
        self.bytes_sent -= (t - self.lasttime) * self.upload_rate
        self.lasttime = t
        if check_time:
            self.bytes_sent = max(self.bytes_sent, 0)
        cur = self.last.next_upload
        while self.bytes_sent <= 0:
            bytes = cur.send_partial(self.unitsize)
            self.bytes_sent += bytes
            self.measure.update_rate(bytes)
            if bytes == 0 or cur.backlogged():
                if self.last is cur:
                    self.last = None
                    cur.next_upload = None
                    break
                else:
                    self.last.next_upload = cur.next_upload
                    cur.next_upload = None
                    cur = self.last.next_upload
            elif not cur.connection.is_coordinator_con(
            ) or not cur.upload.buffer:
                self.last = cur
                cur = cur.next_upload
        else:
            delay = min(5.0, self.bytes_sent / self.upload_rate)
            self.sched(self.try_send, delay)

    def adjust_sent(self, bytes):
        self.bytes_sent = min(self.bytes_sent + bytes, self.upload_rate * 3)
        self.measure.update_rate(bytes)

    def ping(self, delay):
        if DEBUG:
            print >> sys.stderr, delay
        if not self.autoadjust:
            return
        self.pings.append(delay > PING_BOUNDARY)
        if len(self.pings) < PING_SAMPLES + PING_DISCARDS:
            return
        if DEBUG:
            print >> sys.stderr, 'RateLimiter: cycle'
        pings = sum(self.pings[PING_DISCARDS:])
        del self.pings[:]
        if pings >= PING_THRESHHOLD:
            if self.upload_rate == MAX_RATE:
                self.upload_rate = self.measure.get_rate() * ADJUST_DOWN
            else:
                self.upload_rate = min(self.upload_rate,
                                       self.measure.get_rate() * 1.1)
            self.upload_rate = max(int(self.upload_rate * ADJUST_DOWN), 2)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >> sys.stderr, 'RateLimiter: adjust down to ' + str(
                    self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_FIRST
        else:
            if self.upload_rate == MAX_RATE:
                return
            self.autoadjustup -= 1
            if self.autoadjustup:
                return
            self.upload_rate = int(self.upload_rate * ADJUST_UP)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >> sys.stderr, 'RateLimiter: adjust up to ' + str(
                    self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_NEXT
예제 #7
0
class RateLimiter:
    def __init__(self, sched, unitsize, slotsfunc=lambda x: None):
        self.sched = sched
        self.last = None
        self.unitsize = unitsize
        self.slotsfunc = slotsfunc
        self.measure = Measure(MAX_RATE_PERIOD)
        self.autoadjust = False
        self.upload_rate = MAX_RATE * 1000
        self.slots = SLOTS_STARTING  # garbage if not automatic

    def set_upload_rate(self, rate):
        # rate = -1 # test automatic
        if rate < 0:
            if self.autoadjust:
                return
            self.autoadjust = True
            self.autoadjustup = 0
            self.pings = []
            rate = MAX_RATE
            self.slots = SLOTS_STARTING
            self.slotsfunc(self.slots)
        else:
            self.autoadjust = False
        if not rate:
            rate = MAX_RATE
        self.upload_rate = rate * 1000
        self.lasttime = clock()
        self.bytes_sent = 0

    def queue(self, conn):
        assert conn.next_upload is None
        if self.last is None:
            self.last = conn
            conn.next_upload = conn
            self.try_send(True)
        else:
            conn.next_upload = self.last.next_upload
            self.last.next_upload = conn
            # 2fastbt_
            if not conn.connection.is_coordinator_con():
                self.last = conn
# _2fastbt

    def try_send(self, check_time=False):
        t = clock()
        self.bytes_sent -= (t - self.lasttime) * self.upload_rate
        #print 'try_send: bytes_sent: %s' % self.bytes_sent
        self.lasttime = t
        if check_time:
            self.bytes_sent = max(self.bytes_sent, 0)
        cur = self.last.next_upload
        while self.bytes_sent <= 0:
            bytes = cur.send_partial(self.unitsize)
            self.bytes_sent += bytes
            self.measure.update_rate(bytes)
            if bytes == 0 or cur.backlogged():
                if self.last is cur:
                    self.last = None
                    cur.next_upload = None
                    break
                else:
                    self.last.next_upload = cur.next_upload
                    cur.next_upload = None
                    cur = self.last.next_upload
            else:
                # 2fastbt_
                if not cur.connection.is_coordinator_con(
                ) or not cur.upload.buffer:
                    # _2fastbt
                    self.last = cur
                    cur = cur.next_upload
# 2fastbt_
                else:
                    pass
# _2fastbt
        else:
            self.sched(self.try_send, self.bytes_sent / self.upload_rate)

    def adjust_sent(self, bytes):
        self.bytes_sent = min(self.bytes_sent + bytes, self.upload_rate * 3)
        self.measure.update_rate(bytes)

    def ping(self, delay):
        ##raise Exception('Is this called?')
        if DEBUG:
            print delay
        if not self.autoadjust:
            return
        self.pings.append(delay > PING_BOUNDARY)
        if len(self.pings) < PING_SAMPLES + PING_DISCARDS:
            return
        if DEBUG:
            print 'cycle'
        pings = sum(self.pings[PING_DISCARDS:])
        del self.pings[:]
        if pings >= PING_THRESHHOLD:  # assume flooded
            if self.upload_rate == MAX_RATE:
                self.upload_rate = self.measure.get_rate() * ADJUST_DOWN
            else:
                self.upload_rate = min(self.upload_rate,
                                       self.measure.get_rate() * 1.1)
            self.upload_rate = max(int(self.upload_rate * ADJUST_DOWN), 2)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print 'adjust down to ' + str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_FIRST
        else:  # not flooded
            if self.upload_rate == MAX_RATE:
                return
            self.autoadjustup -= 1
            if self.autoadjustup:
                return
            self.upload_rate = int(self.upload_rate * ADJUST_UP)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print 'adjust up to ' + str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_NEXT
예제 #8
0
class SingleDownload:
    def __init__(self, downloader, connection):
        self.downloader = downloader
        self.connection = connection
        # Whether the peer is choking this client.
        self.choked = True
        # Whether this client is interested in data the peer has.
        self.interested = False
        # The (index, begin, length) tuples this client has requested from the peer.
        self.active_requests = []
        # Measures the download rate from the peer.
        self.measure = Measure(downloader.max_rate_period)
        # The pieces the peer has.
        self.have = Bitfield(downloader.numpieces)
        # The last time this client has gotten data from the peer.
        self.last = 0
        self.example_interest = None

    def disconnected(self):
        self.downloader.downloads.remove(self)
        # Decrement the availability of each piece this peer had.
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.lost_have(i)
        self._letgo()

    def _letgo(self):
        if not self.active_requests:
            return
        if self.downloader.storage.is_endgame():
            # If in endgame mode, requesting blocks in active_requests from other peers anyway.
            self.active_requests = []
            return
        # The piece indexes that this client was requesting from the peer.
        lost = []
        for index, begin, length in self.active_requests:
            # No longer downloading this block.
            self.downloader.storage.request_lost(index, begin, length)
            if index not in lost:
                lost.append(index)
        self.active_requests = []
        # Get all other SingleDownload instances that are not choking us.
        ds = [d for d in self.downloader.downloads if not d.choked]
        shuffle(ds)

        for d in ds:
            d._request_more(lost)
        for d in self.downloader.downloads:
            # Get all other SingleDownload instances that are choking us. None of these were in ds.
            # Also, filter by the ones that we are not interested in.
            if d.choked and not d.interested:
                for l in lost:
                    if d.have[l] and self.downloader.storage.do_I_have_requests(l):
                        # This other peer has a piece that the client was downloading from this peer,
                        # so become interested.
                        d.interested = True
                        d.connection.send_interested()
                        break

    def got_choke(self):
        if not self.choked:
            # The peer choked this client.
            self.choked = True
            self._letgo()

    def got_unchoke(self):
        if self.choked:
            # The peer unchoked this client.
            self.choked = False
            if self.interested:
                # This peer has data we want, so request it.
                self._request_more()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def got_piece(self, index, begin, piece):
        try:
            # This active request to the peer has been fulfilled.
            self.active_requests.remove((index, begin, len(piece)))
        except ValueError:
            return False
        if self.downloader.storage.is_endgame():
            # Remove this from the consolidated list of blocks sent to all peers.
            self.downloader.all_requests.remove((index, begin, len(piece)))
        # Update our upload and download rates.
        self.last = time()
        self.measure.update_rate(len(piece))
        self.downloader.measurefunc(len(piece))
        self.downloader.downmeasure.update_rate(len(piece))
        # TODO
        if not self.downloader.storage.piece_came_in(index, begin, piece):
            # This block completed a piece but it failed validation.
            if self.downloader.storage.is_endgame():
                while self.downloader.storage.do_I_have_requests(index):
                    nb, nl = self.downloader.storage.new_request(index)
                    self.downloader.all_requests.append((index, nb, nl))
                for d in self.downloader.downloads:
                    d.fix_download_endgame()
                return False
            # Decrease the priority of this piece...
            self.downloader.picker.bump(index)
            # ... but try downloading this piece again immediately?
            ds = [d for d in self.downloader.downloads if not d.choked]
            shuffle(ds)
            for d in ds:
                d._request_more([index])
            return False
        if self.downloader.storage.do_I_have(index):
            # Notify the picker that this piece is complete.
            self.downloader.picker.complete(index)
        if self.downloader.storage.is_endgame():
            for d in self.downloader.downloads:
                if d is not self and d.interested:
                    if d.choked:
                        # Keep requesting pieces that we're requesting from other peers.
                        d.fix_download_endgame()
                    else:
                        # Cancel the request for this block from this peer.
                        try:
                            d.active_requests.remove((index, begin, len(piece)))
                        except ValueError:
                            # Wasn't requesting this block from this peer.
                            continue
                        d.connection.send_cancel(index, begin, len(piece))
                        # Keep requesting pieces that we're requesting from other peers.
                        d.fix_download_endgame()
        self._request_more()
        if self.downloader.picker.am_I_complete():
            for d in [i for i in self.downloader.downloads if i.have.numfalse == 0]:
                d.connection.close()
        return self.downloader.storage.do_I_have(index)

    def _want(self, index):
        # Want a piece if this user has it and TODO.
        return self.have[index] and self.downloader.storage.do_I_have_requests(index)

    def _request_more(self, indices = None):
        assert not self.choked
        # Return if we already have the maximum outstanding requests to this peer.
        if len(self.active_requests) == self.downloader.backlog:
            return
        if self.downloader.storage.is_endgame():
            # Keep requesting pieces that we're requesting from other peers.
            self.fix_download_endgame()
            return

        lost_interests = []
        while len(self.active_requests) < self.downloader.backlog:
            # Have less than the maximum outstanding requests to this peer...
            if indices is None:
                # Not passed any specific indexes to get. Pick a piece to download.
                interest = self.downloader.picker.next(self._want, self.have.numfalse == 0)
            else:
                # Pick a piece from one of the given indexes to download.
                interest = None
                for i in indices:
                    if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                        interest = i
                        break
            if interest is None:
                # Could not find anything we want, so break.
                break
            if not self.interested:
                # Found a piece we want; tell the peer that this client is interested.
                self.interested = True
                self.connection.send_interested()
            self.example_interest = interest
            # Get a block of the piece to request.
            begin, length = self.downloader.storage.new_request(interest)
            # Notify the PiecePicker that we're requesting this piece.
            self.downloader.picker.requested(interest, self.have.numfalse == 0)
            # Append to the list of all requests, and actually request it.
            self.active_requests.append((interest, begin, length))
            self.connection.send_request(interest, begin, length)
            if not self.downloader.storage.do_I_have_requests(interest):
                # TODO
                lost_interests.append(interest)

        if not self.active_requests and self.interested:
            # Peer has no pieces this client wants, so no longer interested.
            self.interested = False
            self.connection.send_not_interested()

        if lost_interests:
            for d in self.downloader.downloads:
                if d.active_requests or not d.interested:
                    # Looking for a client that has no active requests, but we're interested in.
                    continue
                if d.example_interest is not None and self.downloader.storage.do_I_have_requests(d.example_interest):
                    continue
                for lost in lost_interests:
                    if d.have[lost]:
                        break
                else:
                    continue
                # 
                interest = self.downloader.picker.next(d._want, d.have.numfalse == 0)
                if interest is None:
                    d.interested = False
                    d.connection.send_not_interested()
                else:
                    d.example_interest = interest

        if self.downloader.storage.is_endgame():
            # Now entering endgame mode.
            self.downloader.all_requests = []
            # Consolidate the block requests this client has sent to all peers.
            for d in self.downloader.downloads:
                self.downloader.all_requests.extend(d.active_requests)
            # Request from each peer pieces that we're requesting from other peers.
            for d in self.downloader.downloads:
                d.fix_download_endgame()

    def fix_download_endgame(self):
        # Find pieces this peer has which we're requesting from other peers.
        want = [a for a in self.downloader.all_requests if self.have[a[0]] and a not in self.active_requests]
        if self.interested and not self.active_requests and not want:
            # There are no such pieces, and we're not requesting any others, so become uninterested.
            self.interested = False
            self.connection.send_not_interested()
            return
        if not self.interested and want:
            # There are such pieces, so become interested.
            self.interested = True
            self.connection.send_interested()
        if self.choked:
            # Peer is choking us, so we can't send any requests yet.
            return
        # Don't send exceed the maximum number of requests to the client.
        shuffle(want)
        del want[self.downloader.backlog - len(self.active_requests):]
        # Request the blocks that we're requesting from other peers.
        self.active_requests.extend(want)
        for piece, begin, length in want:
            self.connection.send_request(piece, begin, length)

    def got_have(self, index):
        if self.have[index]:
            # Already knew that the client has this piece.
            return
        self.have[index] = True
        # Increase the availability of this piece.
        self.downloader.picker.got_have(index)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            # Both this client and the peer have every piece, so close.
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            # Keep requesting pieces that we're requesting from other peers.
            self.fix_download_endgame()
        elif self.downloader.storage.do_I_have_requests(index):
            if not self.choked:
                self._request_more([index])
            else:
                # The peer is choking us, but express that this client is now interested.
                if not self.interested:
                    self.interested = True
                    self.connection.send_interested()

    def got_have_bitfield(self, have):
        # Assign the full bitfield of pieces this client has.
        self.have = have
        for i in xrange(len(self.have)):
            # Increase the availability of each piece.
            if self.have[i]:
                self.downloader.picker.got_have(i)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            # Both this client and the peer have every piece, so close.
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            for piece, begin, length in self.downloader.all_requests:
                # Endgame, so want to send a request for any missing blocks to this peer.
                if self.have[piece]:
                    self.interested = True
                    self.connection.send_interested()
                    return
        for i in xrange(len(self.have)):
            # This peer has a piece that we want, so express interest.
            if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                self.interested = True
                self.connection.send_interested()
                return

    def get_rate(self):
        return self.measure.get_rate()

    def is_snubbed(self):
        # Whether significant time has gone by without getting data from a peer.
        return time() - self.last > self.downloader.snub_time
예제 #9
0
class SingleDownload:
    def __init__(self, downloader, connection):
        self.downloader = downloader
        self.connection = connection
        # Whether the peer is choking this client.
        self.choked = True
        # Whether this client is interested in data the peer has.
        self.interested = False
        # The (index, begin, length) tuples this client has requested from the peer.
        self.active_requests = []
        # Measures the download rate from the peer.
        self.measure = Measure(downloader.max_rate_period)
        # The pieces the peer has.
        self.have = Bitfield(downloader.numpieces)
        # The last time this client has gotten data from the peer.
        self.last = 0
        self.example_interest = None

    def disconnected(self):
        self.downloader.downloads.remove(self)
        # Decrement the availability of each piece this peer had.
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.lost_have(i)
        self._letgo()

    def _letgo(self):
        if not self.active_requests:
            return
        if self.downloader.storage.is_endgame():
            # If in endgame mode, requesting blocks in active_requests from other peers anyway.
            self.active_requests = []
            return
        # The piece indexes that this client was requesting from the peer.
        lost = []
        for index, begin, length in self.active_requests:
            # No longer downloading this block.
            self.downloader.storage.request_lost(index, begin, length)
            if index not in lost:
                lost.append(index)
        self.active_requests = []
        # Get all other SingleDownload instances that are not choking us.
        ds = [d for d in self.downloader.downloads if not d.choked]
        shuffle(ds)

        for d in ds:
            d._request_more(lost)
        for d in self.downloader.downloads:
            # Get all other SingleDownload instances that are choking us. None of these were in ds.
            # Also, filter by the ones that we are not interested in.
            if d.choked and not d.interested:
                for l in lost:
                    if d.have[l] and self.downloader.storage.do_I_have_requests(
                            l):
                        # This other peer has a piece that the client was downloading from this peer,
                        # so become interested.
                        d.interested = True
                        d.connection.send_interested()
                        break

    def got_choke(self):
        if not self.choked:
            # The peer choked this client.
            self.choked = True
            self._letgo()

    def got_unchoke(self):
        if self.choked:
            # The peer unchoked this client.
            self.choked = False
            if self.interested:
                # This peer has data we want, so request it.
                self._request_more()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def got_piece(self, index, begin, piece):
        try:
            # This active request to the peer has been fulfilled.
            self.active_requests.remove((index, begin, len(piece)))
        except ValueError:
            return False
        if self.downloader.storage.is_endgame():
            # Remove this from the consolidated list of blocks sent to all peers.
            self.downloader.all_requests.remove((index, begin, len(piece)))
        # Update our upload and download rates.
        self.last = time()
        self.measure.update_rate(len(piece))
        self.downloader.measurefunc(len(piece))
        self.downloader.downmeasure.update_rate(len(piece))
        # TODO
        if not self.downloader.storage.piece_came_in(index, begin, piece):
            # This block completed a piece but it failed validation.
            if self.downloader.storage.is_endgame():
                while self.downloader.storage.do_I_have_requests(index):
                    nb, nl = self.downloader.storage.new_request(index)
                    self.downloader.all_requests.append((index, nb, nl))
                for d in self.downloader.downloads:
                    d.fix_download_endgame()
                return False
            # Decrease the priority of this piece...
            self.downloader.picker.bump(index)
            # ... but try downloading this piece again immediately?
            ds = [d for d in self.downloader.downloads if not d.choked]
            shuffle(ds)
            for d in ds:
                d._request_more([index])
            return False
        if self.downloader.storage.do_I_have(index):
            # Notify the picker that this piece is complete.
            self.downloader.picker.complete(index)
        if self.downloader.storage.is_endgame():
            for d in self.downloader.downloads:
                if d is not self and d.interested:
                    if d.choked:
                        # Keep requesting pieces that we're requesting from other peers.
                        d.fix_download_endgame()
                    else:
                        # Cancel the request for this block from this peer.
                        try:
                            d.active_requests.remove(
                                (index, begin, len(piece)))
                        except ValueError:
                            # Wasn't requesting this block from this peer.
                            continue
                        d.connection.send_cancel(index, begin, len(piece))
                        # Keep requesting pieces that we're requesting from other peers.
                        d.fix_download_endgame()
        self._request_more()
        if self.downloader.picker.am_I_complete():
            for d in [
                    i for i in self.downloader.downloads
                    if i.have.numfalse == 0
            ]:
                d.connection.close()
        return self.downloader.storage.do_I_have(index)

    def _want(self, index):
        # Want a piece if this user has it and TODO.
        return self.have[index] and self.downloader.storage.do_I_have_requests(
            index)

    def _request_more(self, indices=None):
        assert not self.choked
        # Return if we already have the maximum outstanding requests to this peer.
        if len(self.active_requests) == self.downloader.backlog:
            return
        if self.downloader.storage.is_endgame():
            # Keep requesting pieces that we're requesting from other peers.
            self.fix_download_endgame()
            return

        lost_interests = []
        while len(self.active_requests) < self.downloader.backlog:
            # Have less than the maximum outstanding requests to this peer...
            if indices is None:
                # Not passed any specific indexes to get. Pick a piece to download.
                interest = self.downloader.picker.next(self._want,
                                                       self.have.numfalse == 0)
            else:
                # Pick a piece from one of the given indexes to download.
                interest = None
                for i in indices:
                    if self.have[
                            i] and self.downloader.storage.do_I_have_requests(
                                i):
                        interest = i
                        break
            if interest is None:
                # Could not find anything we want, so break.
                break
            if not self.interested:
                # Found a piece we want; tell the peer that this client is interested.
                self.interested = True
                self.connection.send_interested()
            self.example_interest = interest
            # Get a block of the piece to request.
            begin, length = self.downloader.storage.new_request(interest)
            # Notify the PiecePicker that we're requesting this piece.
            self.downloader.picker.requested(interest, self.have.numfalse == 0)
            # Append to the list of all requests, and actually request it.
            self.active_requests.append((interest, begin, length))
            self.connection.send_request(interest, begin, length)
            if not self.downloader.storage.do_I_have_requests(interest):
                # TODO
                lost_interests.append(interest)

        if not self.active_requests and self.interested:
            # Peer has no pieces this client wants, so no longer interested.
            self.interested = False
            self.connection.send_not_interested()

        if lost_interests:
            for d in self.downloader.downloads:
                if d.active_requests or not d.interested:
                    # Looking for a client that has no active requests, but we're interested in.
                    continue
                if d.example_interest is not None and self.downloader.storage.do_I_have_requests(
                        d.example_interest):
                    continue
                for lost in lost_interests:
                    if d.have[lost]:
                        break
                else:
                    continue
                #
                interest = self.downloader.picker.next(d._want,
                                                       d.have.numfalse == 0)
                if interest is None:
                    d.interested = False
                    d.connection.send_not_interested()
                else:
                    d.example_interest = interest

        if self.downloader.storage.is_endgame():
            # Now entering endgame mode.
            self.downloader.all_requests = []
            # Consolidate the block requests this client has sent to all peers.
            for d in self.downloader.downloads:
                self.downloader.all_requests.extend(d.active_requests)
            # Request from each peer pieces that we're requesting from other peers.
            for d in self.downloader.downloads:
                d.fix_download_endgame()

    def fix_download_endgame(self):
        # Find pieces this peer has which we're requesting from other peers.
        want = [
            a for a in self.downloader.all_requests
            if self.have[a[0]] and a not in self.active_requests
        ]
        if self.interested and not self.active_requests and not want:
            # There are no such pieces, and we're not requesting any others, so become uninterested.
            self.interested = False
            self.connection.send_not_interested()
            return
        if not self.interested and want:
            # There are such pieces, so become interested.
            self.interested = True
            self.connection.send_interested()
        if self.choked:
            # Peer is choking us, so we can't send any requests yet.
            return
        # Don't send exceed the maximum number of requests to the client.
        shuffle(want)
        del want[self.downloader.backlog - len(self.active_requests):]
        # Request the blocks that we're requesting from other peers.
        self.active_requests.extend(want)
        for piece, begin, length in want:
            self.connection.send_request(piece, begin, length)

    def got_have(self, index):
        if self.have[index]:
            # Already knew that the client has this piece.
            return
        self.have[index] = True
        # Increase the availability of this piece.
        self.downloader.picker.got_have(index)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            # Both this client and the peer have every piece, so close.
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            # Keep requesting pieces that we're requesting from other peers.
            self.fix_download_endgame()
        elif self.downloader.storage.do_I_have_requests(index):
            if not self.choked:
                self._request_more([index])
            else:
                # The peer is choking us, but express that this client is now interested.
                if not self.interested:
                    self.interested = True
                    self.connection.send_interested()

    def got_have_bitfield(self, have):
        # Assign the full bitfield of pieces this client has.
        self.have = have
        for i in xrange(len(self.have)):
            # Increase the availability of each piece.
            if self.have[i]:
                self.downloader.picker.got_have(i)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            # Both this client and the peer have every piece, so close.
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            for piece, begin, length in self.downloader.all_requests:
                # Endgame, so want to send a request for any missing blocks to this peer.
                if self.have[piece]:
                    self.interested = True
                    self.connection.send_interested()
                    return
        for i in xrange(len(self.have)):
            # This peer has a piece that we want, so express interest.
            if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                self.interested = True
                self.connection.send_interested()
                return

    def get_rate(self):
        return self.measure.get_rate()

    def is_snubbed(self):
        # Whether significant time has gone by without getting data from a peer.
        return time() - self.last > self.downloader.snub_time
예제 #10
0
class RateLimiter:
    def __init__(self, sched, unitsize, slotsfunc=lambda x: None):
        self.sched = sched
        self.last = None
        self.unitsize = unitsize
        self.slotsfunc = slotsfunc
        self.measure = Measure(MAX_RATE_PERIOD)
        self.autoadjust = False
        self.upload_rate = MAX_RATE * 1000
        self.slots = SLOTS_STARTING  # garbage if not automatic

    def set_upload_rate(self, rate):
        if DEBUG:
            print >> sys.stderr, "RateLimiter: set_upload_rate", rate

        # rate = -1 # test automatic
        if rate < 0:
            if self.autoadjust:
                return
            self.autoadjust = True
            self.autoadjustup = 0
            self.pings = []
            rate = MAX_RATE
            self.slots = SLOTS_STARTING
            self.slotsfunc(self.slots)
        else:
            self.autoadjust = False
        if not rate:
            rate = MAX_RATE
        self.upload_rate = rate * 1000
        self.lasttime = clock()
        self.bytes_sent = 0

    def queue(self, conn):
        if DEBUG: print >> sys.stderr, "RateLimiter: queue", conn
        assert conn.next_upload is None
        if self.last is None:
            self.last = conn
            conn.next_upload = conn
            self.try_send(True)
        else:
            conn.next_upload = self.last.next_upload
            self.last.next_upload = conn
            self.last = conn

    def try_send(self, check_time=False):
        if DEBUG: print >> sys.stderr, "RateLimiter: try_send"
        t = clock()
        self.bytes_sent -= (t - self.lasttime) * self.upload_rate
        #print >> sys.stderr, 'try_send: bytes_sent: %s' % self.bytes_sent
        self.lasttime = t
        if check_time and (self.upload_rate < MAX_RATE * 1000):
            #do not set self.bytes_sent to 0 if we are unlimited...
            self.bytes_sent = max(self.bytes_sent, 0)

        cur = self.last.next_upload
        while self.bytes_sent <= 0:
            #we would like to send up to self.bytes_sent data to someone
            #why not try to send this at once?
            #bytes = cur.send_partial(self.unitsize)

            remaining_bytes = max(self.unitsize, int(1 - self.bytes_sent))
            bytes = cur.send_partial(remaining_bytes)

            self.bytes_sent += bytes
            self.measure.update_rate(bytes)
            if bytes == 0 or cur.backlogged():
                if self.last is cur:
                    self.last = None
                    cur.next_upload = None
                    break
                else:
                    self.last.next_upload = cur.next_upload
                    cur.next_upload = None
                    cur = self.last.next_upload
            else:
                #does this connection still have a buffer?
                if not cur.upload.buffer:
                    self.last = cur
                    cur = cur.next_upload  #switch to the next one
                else:
                    pass
        else:
            # 01/04/10 Boudewijn: because we use a -very- small value
            # to indicate a 0bps rate, we will schedule the call to be
            # made in a very long time.  This results in no upload for
            # a very long time.
            #
            # the try_send method has protection again calling to
            # soon, so we can simply schedule the call to be made
            # sooner.
            delay = min(5.0, self.bytes_sent / self.upload_rate)
            self.sched(self.try_send, delay)

    def adjust_sent(self, bytes):
        # if DEBUG: print >>sys.stderr, "RateLimiter: adjust_sent", bytes
        self.bytes_sent = min(self.bytes_sent + bytes, self.upload_rate * 3)
        self.measure.update_rate(bytes)

    def ping(self, delay):
        ##raise Exception('Is this called?')
        if DEBUG:
            print >> sys.stderr, delay
        if not self.autoadjust:
            return
        self.pings.append(delay > PING_BOUNDARY)
        if len(self.pings) < PING_SAMPLES + PING_DISCARDS:
            return
        if DEBUG:
            print >> sys.stderr, 'RateLimiter: cycle'
        pings = sum(self.pings[PING_DISCARDS:])
        del self.pings[:]
        if pings >= PING_THRESHHOLD:  # assume flooded
            if self.upload_rate == MAX_RATE:
                self.upload_rate = self.measure.get_rate() * ADJUST_DOWN
            else:
                self.upload_rate = min(self.upload_rate,
                                       self.measure.get_rate() * 1.1)
            self.upload_rate = max(int(self.upload_rate * ADJUST_DOWN), 2)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >> sys.stderr, 'RateLimiter: adjust down to ' + str(
                    self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_FIRST
        else:  # not flooded
            if self.upload_rate == MAX_RATE:
                return
            self.autoadjustup -= 1
            if self.autoadjustup:
                return
            self.upload_rate = int(self.upload_rate * ADJUST_UP)
            self.slots = int(sqrt(self.upload_rate * SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >> sys.stderr, 'RateLimiter: adjust up to ' + str(
                    self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_NEXT
예제 #11
0
파일: Downloader.py 프로젝트: myliang/works
class SingleDownload:
    def __init__(self, downloader, connection):
        self.downloader = downloader
        self.connection = connection
        # remote choked local
        self.choked = True
        # local is not interest remote
        self.interested = False

        # active request list
        self.active_requests = []

        # measure down rate
        self.measure = Measure(downloader.max_rate_period)

        # bitfield
        self.have = Bitfield(downloader.numpieces)

        self.last = 0

        self.example_interest = None

    # disconnected
    def disconnected(self):
        # remove self from downloads array
        self.downloader.downloads.remove(self)
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.lost_have(i)
        self._letgo()

    def _letgo(self):
        if not self.active_requests:
            return
        if self.downloader.storage.is_endgame():
            self.active_requests = []
            return
        lost = []
        for index, begin, length in self.active_requests:
            self.downloader.storage.request_lost(index, begin, length)
            if index not in lost:
                lost.append(index)
        self.active_requests = []
        ds = [d for d in self.downloader.downloads if not d.choked]
        shuffle(ds)
        for d in ds:
            d._request_more(lost)
        for d in self.downloader.downloads:
            if d.choked and not d.interested:
                for l in lost:
                    if d.have[l] and self.downloader.storage.do_I_have_requests(l):
                        d.interested = True
                        d.connection.send_interested()
                        break

    def got_choke(self):
        if not self.choked:
            self.choked = True
            self._letgo()

    def got_unchoke(self):
        if self.choked:
            self.choked = False
            if self.interested:
                self._request_more()

    def is_choked(self):
        return self.choked

    def is_interested(self):
        return self.interested

    def got_piece(self, index, begin, piece):
        try:
            self.active_requests.remove((index, begin, len(piece)))
        except ValueError:
            return False
        if self.downloader.storage.is_endgame():
            self.downloader.all_requests.remove((index, begin, len(piece)))
        self.last = time()
        self.measure.update_rate(len(piece))
        self.downloader.measurefunc(len(piece))
        self.downloader.downmeasure.update_rate(len(piece))
        if not self.downloader.storage.piece_came_in(index, begin, piece):
            if self.downloader.storage.is_endgame():
                while self.downloader.storage.do_I_have_requests(index):
                    nb, nl = self.downloader.storage.new_request(index)
                    self.downloader.all_requests.append((index, nb, nl))
                for d in self.downloader.downloads:
                    d.fix_download_endgame()
                return False
            self.downloader.picker.bump(index)
            ds = [d for d in self.downloader.downloads if not d.choked]
            shuffle(ds)
            for d in ds:
                d._request_more([index])
            return False
        if self.downloader.storage.do_I_have(index):
            self.downloader.picker.complete(index)
        if self.downloader.storage.is_endgame():
            for d in self.downloader.downloads:
                if d is not self and d.interested:
                    if d.choked:
                        d.fix_download_endgame()
                    else:
                        try:
                            d.active_requests.remove((index, begin, len(piece)))
                        except ValueError:
                            continue
                        d.connection.send_cancel(index, begin, len(piece))
                        d.fix_download_endgame()
        self._request_more()
        if self.downloader.picker.am_I_complete():
            for d in [i for i in self.downloader.downloads if i.have.numfalse == 0]:
                d.connection.close()
        return self.downloader.storage.do_I_have(index)

    def _want(self, index):
        return self.have[index] and self.downloader.storage.do_I_have_requests(index)

    def _request_more(self, indices = None):
        assert not self.choked
        if len(self.active_requests) == self.downloader.backlog:
            return
        if self.downloader.storage.is_endgame():
            self.fix_download_endgame()
            return
        lost_interests = []
        while len(self.active_requests) < self.downloader.backlog:
            if indices is None:
                interest = self.downloader.picker.next(self._want, self.have.numfalse == 0)
            else:
                interest = None
                for i in indices:
                    if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                        interest = i
                        break
            if interest is None:
                break
            if not self.interested:
                self.interested = True
                self.connection.send_interested()
            self.example_interest = interest
            begin, length = self.downloader.storage.new_request(interest)
            self.downloader.picker.requested(interest, self.have.numfalse == 0)
            self.active_requests.append((interest, begin, length))
            self.connection.send_request(interest, begin, length)
            if not self.downloader.storage.do_I_have_requests(interest):
                lost_interests.append(interest)
        if not self.active_requests and self.interested:
            self.interested = False
            self.connection.send_not_interested()
        if lost_interests:
            for d in self.downloader.downloads:
                if d.active_requests or not d.interested:
                    continue
                if d.example_interest is not None and self.downloader.storage.do_I_have_requests(d.example_interest):
                    continue
                for lost in lost_interests:
                    if d.have[lost]:
                        break
                else:
                    continue
                interest = self.downloader.picker.next(d._want, d.have.numfalse == 0)
                if interest is None:
                    d.interested = False
                    d.connection.send_not_interested()
                else:
                    d.example_interest = interest
        if self.downloader.storage.is_endgame():
            self.downloader.all_requests = []
            for d in self.downloader.downloads:
                self.downloader.all_requests.extend(d.active_requests)
            for d in self.downloader.downloads:
                d.fix_download_endgame()

    def fix_download_endgame(self):
        want = [a for a in self.downloader.all_requests if self.have[a[0]] and a not in self.active_requests]
        if self.interested and not self.active_requests and not want:
            self.interested = False
            self.connection.send_not_interested()
            return
        if not self.interested and want:
            self.interested = True
            self.connection.send_interested()
        if self.choked:
            return
        shuffle(want)
        del want[self.downloader.backlog - len(self.active_requests):]
        self.active_requests.extend(want)
        for piece, begin, length in want:
            self.connection.send_request(piece, begin, length)

    def got_have(self, index):
        if self.have[index]:
            return
        self.have[index] = True
        self.downloader.picker.got_have(index)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            self.fix_download_endgame()
        elif self.downloader.storage.do_I_have_requests(index):
            if not self.choked:
                self._request_more([index])
            else:
                if not self.interested:
                    self.interested = True
                    self.connection.send_interested()

    def got_have_bitfield(self, have):
        self.have = have
        for i in xrange(len(self.have)):
            if self.have[i]:
                self.downloader.picker.got_have(i)
        if self.downloader.picker.am_I_complete() and self.have.numfalse == 0:
            self.connection.close()
            return
        if self.downloader.storage.is_endgame():
            for piece, begin, length in self.downloader.all_requests:
                if self.have[piece]:
                    self.interested = True
                    self.connection.send_interested()
                    return
        for i in xrange(len(self.have)):
            if self.have[i] and self.downloader.storage.do_I_have_requests(i):
                self.interested = True
                self.connection.send_interested()
                return

    def get_rate(self):
        return self.measure.get_rate()

    def is_snubbed(self):
        return time() - self.last > self.downloader.snub_time
예제 #12
0
class RateLimiter:
    def __init__(self, sched, unitsize, slotsfunc = lambda x: None):
        self.sched = sched
        self.last = None
        self.unitsize = unitsize
        self.slotsfunc = slotsfunc
        self.measure = Measure(MAX_RATE_PERIOD)
        self.autoadjust = False
        self.upload_rate = MAX_RATE * 1000
        self.slots = SLOTS_STARTING    # garbage if not automatic

    def set_upload_rate(self, rate):
        if DEBUG: 
            print >>sys.stderr, time.asctime(),'-', "RateLimiter: set_upload_rate", rate
            
        # rate = -1 # test automatic
        if rate < 0:
            if self.autoadjust:
                return
            self.autoadjust = True
            self.autoadjustup = 0
            self.pings = []
            rate = MAX_RATE
            self.slots = SLOTS_STARTING
            self.slotsfunc(self.slots)
        else:
            self.autoadjust = False
        if not rate:
            rate = MAX_RATE
        self.upload_rate = rate * 1000
        self.lasttime = clock()
        self.bytes_sent = 0

    def queue(self, conn):
        if DEBUG: print >>sys.stderr, time.asctime(),'-', "RateLimiter: queue", conn
        assert conn.next_upload is None
        if self.last is None:
            self.last = conn
            conn.next_upload = conn
            self.try_send(True)
        else:
            conn.next_upload = self.last.next_upload
            self.last.next_upload = conn
# 2fastbt_
            if not conn.connection.is_coordinator_con():
                self.last = conn
# _2fastbt

    def try_send(self, check_time = False):
        if DEBUG: print >>sys.stderr, time.asctime(),'-', "RateLimiter: try_send"
        t = clock()
        self.bytes_sent -= (t - self.lasttime) * self.upload_rate
        #print 'try_send: bytes_sent: %s' % self.bytes_sent
        self.lasttime = t
        if check_time:
            self.bytes_sent = max(self.bytes_sent, 0)
        cur = self.last.next_upload
        while self.bytes_sent <= 0:
            bytes = cur.send_partial(self.unitsize)
            self.bytes_sent += bytes
            self.measure.update_rate(bytes)
            if bytes == 0 or cur.backlogged():
                if self.last is cur:
                    self.last = None
                    cur.next_upload = None
                    break
                else:
                    self.last.next_upload = cur.next_upload
                    cur.next_upload = None
                    cur = self.last.next_upload
            else:
# 2fastbt_
                if not cur.connection.is_coordinator_con() or not cur.upload.buffer:
# _2fastbt
                    self.last = cur
                    cur = cur.next_upload
# 2fastbt_
                else:
                    pass
# _2fastbt
        else:
            # 01/04/10 Boudewijn: because we use a -very- small value
            # to indicate a 0bps rate, we will schedule the call to be
            # made in a very long time.  This results in no upload for
            # a very long time.
            #
            # the try_send method has protection again calling to
            # soon, so we can simply schedule the call to be made
            # sooner.
            delay = min(5.0, self.bytes_sent / self.upload_rate)
            self.sched(self.try_send, delay)

    def adjust_sent(self, bytes):
        # if DEBUG: print >>sys.stderr, time.asctime(),'-', "RateLimiter: adjust_sent", bytes
        self.bytes_sent = min(self.bytes_sent+bytes, self.upload_rate*3)
        self.measure.update_rate(bytes)


    def ping(self, delay):
        ##raise Exception('Is this called?')
        if DEBUG:
            print >>sys.stderr, time.asctime(),'-', delay
        if not self.autoadjust:
            return
        self.pings.append(delay > PING_BOUNDARY)
        if len(self.pings) < PING_SAMPLES+PING_DISCARDS:
            return
        if DEBUG:
            print >>sys.stderr, time.asctime(),'-', 'RateLimiter: cycle'
        pings = sum(self.pings[PING_DISCARDS:])
        del self.pings[:]
        if pings >= PING_THRESHHOLD:   # assume flooded
            if self.upload_rate == MAX_RATE:
                self.upload_rate = self.measure.get_rate()*ADJUST_DOWN
            else:
                self.upload_rate = min(self.upload_rate, 
                                       self.measure.get_rate()*1.1)
            self.upload_rate = max(int(self.upload_rate*ADJUST_DOWN), 2)
            self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >>sys.stderr, time.asctime(),'-', 'RateLimiter: adjust down to '+str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_FIRST
        else:   # not flooded
            if self.upload_rate == MAX_RATE:
                return
            self.autoadjustup -= 1
            if self.autoadjustup:
                return
            self.upload_rate = int(self.upload_rate*ADJUST_UP)
            self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
            self.slotsfunc(self.slots)
            if DEBUG:
                print >>sys.stderr, time.asctime(),'-', 'RateLimiter: adjust up to '+str(self.upload_rate)
            self.lasttime = clock()
            self.bytes_sent = 0
            self.autoadjustup = UP_DELAY_NEXT
예제 #13
0
class Upload:
    def __init__(self, connection, choker, storage, 
            max_slice_length, max_rate_period, fudge):
        self.connection = connection
        self.choker = choker
        self.storage = storage
        self.max_slice_length = max_slice_length
        self.max_rate_period = max_rate_period
        # Whether this client is choking the peer.
        self.choked = True
        # Whether the peer is interested in data on this client.
        self.interested = False
        # (index, begin, length) tuples the peer has requested from this client.
        self.buffer = []
        self.measure = Measure(max_rate_period, fudge)
        # Send our bitfield to the peer if we have any pieces.
        if storage.do_I_have_anything():
            connection.send_bitfield(storage.get_have_list())

    def got_not_interested(self):
        # The peer isn't interested in our data.
        if self.interested:
            self.interested = False
            # Don't proceed to send data that was previously requested.
            del self.buffer[:]
            self.choker.not_interested(self.connection)

    def got_interested(self):
        if not self.interested:
            self.interested = True
            self.choker.interested(self.connection)

    def flushed(self):
        # Send each requested (index, begin, length) part until there is backpressure.
        while len(self.buffer) > 0 and self.connection.is_flushed():
            index, begin, length = self.buffer[0]
            del self.buffer[0]
            piece = self.storage.get_piece(index, begin, length)
            if piece is None:
                # The peer requested a bad piece, so we're done.
                self.connection.close()
                return
            self.measure.update_rate(len(piece))
            self.connection.send_piece(index, begin, piece)

    def got_request(self, index, begin, length):
        if not self.interested or length > self.max_slice_length:
            # The peer requested data without sending an interested message first.
            # Or the peer is requesting too much data.
            self.connection.close()
            return
        if not self.choked:
            # We're not choking this peer, so enqueue and then try to fulfill the request.
            self.buffer.append((index, begin, length))
            self.flushed()

    def got_cancel(self, index, begin, length):
        try:
            # The peer canceled a request.
            self.buffer.remove((index, begin, length))
        except ValueError:
            pass

    def choke(self):
        if not self.choked:
            # This client is now choking the peer, so cancel all outstanding requests.
            self.choked = True
            del self.buffer[:]
            # Notify the peer that it has been choked by this client.
            self.connection.send_choke()

    def unchoke(self):
        if self.choked:
            # This client is no longer choking the peer.
            self.choked = False
            # Notify the peer that it is no longer choked by this client.
            self.connection.send_unchoke()
        
    def is_choked(self):
        return self.choked
        
    def is_interested(self):
        return self.interested

    def has_queries(self):
        return len(self.buffer) > 0

    def get_rate(self):
        return self.measure.get_rate()