Read BitTorrent code diary 5

Source: Internet
Author: User

# Written by Bram Cohen
# See license.txt for license information

# File name: rewserver. py
# Code reading diary:
# Pen: zfive5
#
# Analysis rawserver class is mainly responsible for socket communication

# 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. FDIs the file # descriptor, and
EventIs a bitmask with bits set for the reported events for that descriptor --
# PollinFor waiting input, PolloutTo 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
# Report. If TimeoutIs given, it specifies the length of time in milliseconds which the system will
# Wait for events before returning. If TimeoutIs 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
Failed t 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 class: encapsulates the basic socket class, which is also the basis of communication
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
# Obtain the IP address of the connection peer
Def get_ip (Self ):
Try:
Return self. Socket. getpeername () [0]
Failed t socket. Error:
Return 'no connection'

# Close the socket connection. This is a bit like the closesocket () function of window.
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 ()

# This is needless to say. It's shutdown ().
Def Shutdown (self, Val ):
Self. Socket. Shutdown (VAL)

# Determine whether the buffer list is empty
Def is_flushed (Self ):
Return Len (self. buffer) = 0

# The function is to write data to the buffer list. When the list element is 1, data is sent.
# Connect to the other party
Def write (self, S ):
Assert self. socket is not none
Self. Buffer. append (s)
If Len (self. buffer) = 1:
Self. try_write ()

# Send data to the recipient
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]
Failed t socket. error, E:
Code, MSG = E
If code! = Ewouldblock:
Self. raw_server.dead_from_write.append (Self)
Return
# If the buffer is empty, only read data events are registered.
If self. Buffer = []:
Self. raw_server.poll.register (self. socket, Pollin)
Else:
# Register both read and write
Self. raw_server.poll.register (self. socket, all)

# Server type
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)

# Add a task to the task list. The last running time is time () + delay.
Def add_task (self, func, delay ):
Insort (self. funcs, (Time () + delay, func ))

# Add an additional task to the task list. The last running time is time () + delay.
Def external_add_task (self, func, delay = 0 ):
Self. externally_added.append (func, delay ))

# Check the server timeout socket function. If the timeout is reached, disable the handler function.
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)

# Bind a port and set the non-blocking mode. The server socket corresponds to the event registration
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

# Connect to the other party and set the non-blocking mode. The socket corresponds to event registration.
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)
Failed t socket. Error:
Raise
Except t 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

# Process the server and all data requests connected to the server. events is the list of socket and event-related requests.
Def handle_events (self, events ):
For sock, event in events:
If sock = self. server. fileno ():
# Disabling the server by mistake
If event & (pollhup | pollerr )! = 0:
Self. Poll. unregister (self. Server)
Self. server. Close ()
Self. errorfunc ('lost server socket ')
Else:
# Server Accept
Try:
Newsock, ADDR = self. server. Accept ()
Newsock. setblocking (0)
NSS = singlesocket (self, newsock, self. Handler)
Self. single_sockets [newsock. fileno ()] = NSS
# Register a read data event
Self. Poll. Register (newsock, Pollin)
Self. handler. external_connection_made (NSS)
Failed t socket. Error:
# Accept failed sleep (1), and then continue
Sleep (1)
Else:
S = self. single_sockets.get (sock)
If S is none:
Continue
S. Connected = true
# Disable socket by mistake
If (event & (pollhup | pollerr ))! = 0:
Self. _ close_socket (s)
Continue

# Read Data Processing
If (event & Pollin )! = 0:
Try:
S. last_hit = Time ()
Data = S. Socket. Recv (100000)
If data = '':
Self. _ close_socket (s)
Else:
# Call a processing function
S. handler. data_came_in (S, data)
Failed t socket. error, E:
Code, MSG = E
If code! = Ewouldblock:
Self. _ close_socket (s)
Continue
# Write Data Processing Process
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)
# Pop-up additional tasks
Def pop_external (Self ):
Try:
While true:
(A, B) = self. externally_added.pop ()
Self. add_task (A, B)
Failed t indexerror:
Pass

# The processing function called in download, which is mainly used to complete data processing and call processing.
Def listen_forever (self, Handler ):
Self. Handler = Handler
Try:
# Exit if the completion mark is executed
While not self. doneflag. isset ():
Try:
# Add an additional task
Self. pop_external ()

# The task list is empty.
If Len (self. funcs) = 0:
Period = 2 ** 30
Else:
# The task list is not empty.
Period = self. funcs [0] [0]-Time ()

If the interval is less than zero, the interval is zero.
If period <0:
Period = 0

# Setting registration event timeout
Events = self. Poll. Poll (period * timemult)

# Exit if the completion mark is executed
If self. doneflag. isset ():
Return

# Call the task processing function
While Len (self. funcs)> 0 and self. funcs [0] [0] <= Time ():
Garbage, func = self. funcs [0]
Del self. funcs [0]
Try:
Func ()
Except t keyboardinterrupt:
Print_exc ()
Return
Except t:
If self. Noisy:
Data = stringio ()
Print_exc (file = data)
Self. errorfunc (data. getvalue ())

# Disable socket without reflection
Self. _ close_dead ()

# Receiving and sending socket requests or data
Self. handle_events (events)

# Exit if the completion mark is executed
If self. doneflag. isset ():
Return

# Disable socket without reflection
Self. _ close_dead ()
Failed T error:
If self. doneflag. isset ():
Return
Except t keyboardinterrupt:
Print_exc ()
Return
Except t:
Data = stringio ()
Print_exc (file = data)
Self. errorfunc (data. getvalue ())
Finally:
For SS in self. single_sockets.values ():
SS. Close ()
Self. server. Close ()

# Closing dead connections
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)
# Disable socket connection
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)

# The following is a test case. If this parameter is left blank, the storage class is left for analysis next time .....

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.