一、sockethtml
二、IO多路複用python
三、socketserver瀏覽器
socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,對於文件用【打開】【讀寫】【關閉】模式來操做。socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉)服務器
基本上,Socket 是任何一種計算機網絡通信中最基礎的內容。例如當你在瀏覽器地址欄中輸入 http://www.cnblogs.com/ 時,你會打開一個套接字,而後鏈接到 http://www.cnblogs.com/ 並讀取響應的頁面而後而後顯示出來。而其餘一些聊天客戶端如 gtalk 和 skype 也是相似。任何網絡通信都是經過 Socket 來完成的。網絡
Python 官方關於 Socket 的函數請看 http://docs.python.org/library/socket.html多線程
socket和file的區別:併發
一、file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】app
二、socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】less
那咱們就先來建立一個socket服務端吧socket
import socket sk = socket.socket() sk.bind(("127.0.0.1",8080)) sk.listen(5) conn,address = sk.accept() sk.sendall(bytes("Hello world",encoding="utf-8"))
import socket obj = socket.socket() obj.connect(("127.0.0.1",8080)) ret = str(obj.recv(1024),encoding="utf-8") print(ret)
socket更多功能
def bind(self, address): # real signature unknown; restored from __doc__ """ bind(address) Bind the socket to a local address. For IP sockets, the address is a pair (host, port); the host must refer to the local host. For raw packet sockets the address is a tuple (ifname, proto [,pkttype [,hatype]]) """ '''將套接字綁定到本地地址。是一個IP套接字的地址對(主機、端口),主機必須參考本地主機。''' pass def close(self): # real signature unknown; restored from __doc__ """ close() Close the socket. It cannot be used after this call. """ '''關閉socket''' pass def connect(self, address): # real signature unknown; restored from __doc__ """ connect(address) Connect the socket to a remote address. For IP sockets, the address is a pair (host, port). """ '''將套接字鏈接到遠程地址。IP套接字的地址''' pass def connect_ex(self, address): # real signature unknown; restored from __doc__ """ connect_ex(address) -> errno This is like connect(address), but returns an error code (the errno value) instead of raising an exception when an error occurs. """ pass def detach(self): # real signature unknown; restored from __doc__ """ detach() Close the socket object without closing the underlying file descriptor. The object cannot be used after this call, but the file descriptor can be reused for other purposes. The file descriptor is returned. """ '''關閉套接字對象沒有關閉底層的文件描述符。''' pass def fileno(self): # real signature unknown; restored from __doc__ """ fileno() -> integer Return the integer file descriptor of the socket. """ '''返回整數的套接字的文件描述符。''' return 0 def getpeername(self): # real signature unknown; restored from __doc__ """ getpeername() -> address info Return the address of the remote endpoint. For IP sockets, the address info is a pair (hostaddr, port). """ '''返回遠程端點的地址。IP套接字的地址''' pass def getsockname(self): # real signature unknown; restored from __doc__ """ getsockname() -> address info Return the address of the local endpoint. For IP sockets, the address info is a pair (hostaddr, port). """ '''返回遠程端點的地址。IP套接字的地址''' pass def getsockopt(self, level, option, buffersize=None): # real signature unknown; restored from __doc__ """ getsockopt(level, option[, buffersize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. """ '''獲得一個套接字選項''' pass def gettimeout(self): # real signature unknown; restored from __doc__ """ gettimeout() -> timeout Returns the timeout in seconds (float) associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled. """ '''返回的超時秒數(浮動)與套接字相關聯''' return timeout def ioctl(self, cmd, option): # real signature unknown; restored from __doc__ """ ioctl(cmd, option) -> long Control the socket with WSAIoctl syscall. Currently supported 'cmd' values are SIO_RCVALL: 'option' must be one of the socket.RCVALL_* constants. SIO_KEEPALIVE_VALS: 'option' is a tuple of (onoff, timeout, interval). """ return 0 def listen(self, backlog=None): # real signature unknown; restored from __doc__ """ listen([backlog]) Enable a server to accept connections. If backlog is specified, it must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections. If not specified, a default reasonable value is chosen. """ '''使服務器可以接受鏈接。''' pass def recv(self, buffersize, flags=None): # real signature unknown; restored from __doc__ """ recv(buffersize[, flags]) -> data Receive up to buffersize bytes from the socket. For the optional flags argument, see the Unix manual. When no data is available, block until at least one byte is available or until the remote end is closed. When the remote end is closed and all data is read, return the empty string. """ '''當沒有數據可用,阻塞,直到至少一個字節是可用的或遠程結束以前關閉。''' pass def recvfrom(self, buffersize, flags=None): # real signature unknown; restored from __doc__ """ recvfrom(buffersize[, flags]) -> (data, address info) Like recv(buffersize, flags) but also return the sender's address info. """ pass def recvfrom_into(self, buffer, nbytes=None, flags=None): # real signature unknown; restored from __doc__ """ recvfrom_into(buffer[, nbytes[, flags]]) -> (nbytes, address info) Like recv_into(buffer[, nbytes[, flags]]) but also return the sender's address info. """ pass def recv_into(self, buffer, nbytes=None, flags=None): # real signature unknown; restored from __doc__ """ recv_into(buffer, [nbytes[, flags]]) -> nbytes_read A version of recv() that stores its data into a buffer rather than creating a new string. Receive up to buffersize bytes from the socket. If buffersize is not specified (or 0), receive up to the size available in the given buffer. See recv() for documentation about the flags. """ pass def send(self, data, flags=None): # real signature unknown; restored from __doc__ """ send(data[, flags]) -> count Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent; this may be less than len(data) if the network is busy. """ '''發送一個數據字符串到套接字。''' pass def sendall(self, data, flags=None): # real signature unknown; restored from __doc__ """ sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent. """ '''發送一個數據字符串到套接字,直到全部數據發送完成''' pass def sendto(self, data, flags=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ """ sendto(data[, flags], address) -> count Like send(data, flags) but allows specifying the destination address. For IP sockets, the address is a pair (hostaddr, port). """ pass def setblocking(self, flag): # real signature unknown; restored from __doc__ """ setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0). """ '''是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。''' pass def setsockopt(self, level, option, value): # real signature unknown; restored from __doc__ """ setsockopt(level, option, value) Set a socket option. See the Unix manual for level and option. The value argument can either be an integer or a string. """ pass def settimeout(self, timeout): # real signature unknown; restored from __doc__ """ settimeout(timeout) Set a timeout on socket operations. 'timeout' can be a float, giving in seconds, or None. Setting a timeout of None disables the timeout feature and is equivalent to setblocking(1). Setting a timeout of zero is the same as setblocking(0). """ pass def share(self, process_id): # real signature unknown; restored from __doc__ """ share(process_id) -> bytes Share the socket with another process. The target process id must be provided and the resulting bytes object passed to the target process. There the shared socket can be instantiated by calling socket.fromshare(). """ return b"" def shutdown(self, flag): # real signature unknown; restored from __doc__ """ shutdown(flag) Shut down the reading side of the socket (flag == SHUT_RD), the writing side of the socket (flag == SHUT_WR), or both ends (flag == SHUT_RDWR). """ pass def _accept(self): # real signature unknown; restored from __doc__ """ _accept() -> (integer, address info) Wait for an incoming connection. Return a new socket file descriptor representing the connection, and the address of the client. For IP sockets, the address info is a pair (hostaddr, port). """ pass
注:擼主知道你們懶,因此把所有功能的中文標記在每一個功能的下面啦。下面擼主列一些常常用到的吧
sk.bind(address)
s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。
sk.listen(backlog)
開始監聽傳入鏈接。backlog指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。
backlog等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5
這個值不能無限大,由於要在內核中維護鏈接隊列
sk.setblocking(bool)
是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。
sk.accept()
接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。
接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來
sk.connect(address)
鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。
sk.connect_ex(address)
同上,只不過會有返回值,鏈接成功時返回 0 ,鏈接失敗時候返回編碼,例如:10061
sk.close()
關閉套接字
sk.recv(bufsize[,flag])
接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。
sk.recvfrom(bufsize[.flag])
與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
sk.send(string[,flag])
將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。
sk.sendall(string[,flag])
將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。
內部經過遞歸調用send,將全部內容發送出去。
sk.sendto(string[,flag],address)
將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。
sk.settimeout(timeout)
設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如 client 鏈接最多等待5s )
sk.getpeername()
返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。
sk.getsockname()
返回套接字本身的地址。一般是一個元組(ipaddr,port)
sk.fileno()
套接字的文件描述符
TCP:
import socketserver 服務端 class Myserver(socketserver.BaseRequestHandler): def handle(self): conn = self.request conn.sendall(bytes("你好,我是機器人",encoding="utf-8")) while True: ret_bytes = conn.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") if ret_str == "q": break conn.sendall(bytes(ret_str+"你好我好你們好",encoding="utf-8")) if __name__ == "__main__": server = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Myserver) server.serve_forever() 客戶端 import socket obj = socket.socket() obj.connect(("127.0.0.1",8080)) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str) while True: inp = input("你好請問您有什麼問題? \n >>>") if inp == "q": obj.sendall(bytes(inp,encoding="utf-8")) break else: obj.sendall(bytes(inp, encoding="utf-8")) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str)
服務端 import socket sk = socket.socket() sk.bind(("127.0.0.1",8080)) sk.listen(5) while True: conn,address = sk.accept() conn.sendall(bytes("歡迎光臨我愛我家",encoding="utf-8")) size = conn.recv(1024) size_str = str(size,encoding="utf-8") file_size = int(size_str) conn.sendall(bytes("開始傳送", encoding="utf-8")) has_size = 0 f = open("db_new.jpg","wb") while True: if file_size == has_size: break date = conn.recv(1024) f.write(date) has_size += len(date) f.close() 客戶端 import socket import os obj = socket.socket() obj.connect(("127.0.0.1",8080)) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str) size = os.stat("yan.jpg").st_size obj.sendall(bytes(str(size),encoding="utf-8")) obj.recv(1024) with open("yan.jpg","rb") as f: for line in f: obj.sendall(line)
UdP
import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) sk.bind(ip_port) while True: data = sk.recv(1024) print data import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) while True: inp = input('數據:').strip() if inp == 'exit': break sk.sendto(bytes(inp,encoding = "utf-8"),ip_port) sk.close()
WEB服務應用:
#!/usr/bin/env python #coding:utf-8 import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, World") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8080)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
I/O(input/output),即輸入/輸出端口。每一個設備都會有一個專用的I/O地址,用來處理本身的輸入輸出信息首先什麼是I/O:
I/O分爲磁盤io和網絡io,這裏說的是網絡io
IO多路複用:
I/O多路複用指:經過一種機制,能夠監視多個描述符(socket),一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫操做。
Linux
Linux中的 select,poll,epoll 都是IO多路複用的機制。
Linux下網絡I/O使用socket套接字來通訊,普通I/O模型只能監聽一個socket,而I/O多路複用可同時監聽多個socket.
I/O多路複用避免阻塞在io上,本來爲多進程或多線程來接收多個鏈接的消息變爲單進程或單線程保存多個socket的狀態後輪詢處理.
Python
Python中有一個select模塊,其中提供了:select、poll、epoll三個方法,分別調用系統的 select,poll,epoll 從而實現IO多路複用。
Windows Python: 提供: select Mac Python: 提供: select Linux Python: 提供: select、poll、epoll
對於select模塊操做的方法:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超時時間) 參數: 可接受四個參數(前三個必須) 返回值:三個列表 select方法用來監視文件句柄,若是句柄發生變化,則獲取該句柄。 一、當 參數1 序列中的句柄發生可讀時(accetp和read),則獲取發生變化的句柄並添加到 返回值1 序列中 二、當 參數2 序列中含有句柄時,則將該序列中全部的句柄添加到 返回值2 序列中 三、當 參數3 序列中的句柄發生錯誤時,則將該發生錯誤的句柄添加到 返回值3 序列中 四、當 超時時間 未設置,則select會一直阻塞,直到監聽的句柄發生變化 五、當 超時時間 = 1時,那麼若是監聽的句柄均無任何變化,則select會阻塞 1 秒,以後返回三個空列表,若是監聽的句柄有變化,則直接執行。
import socket import select sk1 = socket.socket() sk1.bind(("127.0.0.1",8001)) sk1.listen() sk2 = socket.socket() sk2.bind(("127.0.0.1",8002)) sk2.listen() sk3 = socket.socket() sk3.bind(("127.0.0.1",8003)) sk3.listen() li = [sk1,sk2,sk3] while True: r_list,w_list,e_list = select.select(li,[],[],1) # r_list可變化的 for line in r_list: conn,address = line.accept() conn.sendall(bytes("Hello World !",encoding="utf-8"))
服務端: sk1 = socket.socket() sk1.bind(("127.0.0.1",8001)) sk1.listen() inpu = [sk1,] while True: r_list,w_list,e_list = select.select(inpu,[],[],1) for sk in r_list: if sk == sk1: conn,address = sk.accept() inpu.append(conn) else: try: ret = str(sk.recv(1024),encoding="utf-8") sk.sendall(bytes(ret+"hao",encoding="utf-8")) except Exception as ex: inpu.remove(sk) 客戶端 import socket obj = socket.socket() obj.connect(('127.0.0.1',8001)) while True: inp = input("Please(q\退出):\n>>>") obj.sendall(bytes(inp,encoding="utf-8")) if inp == "q": break ret = str(obj.recv(1024),encoding="utf-8") print(ret)
服務端: import socket sk1 = socket.socket() sk1.bind(("127.0.0.1",8001)) sk1.listen() inputs = [sk1] import select message_dic = {} outputs = [] while True: r_list, w_list, e_list = select.select(inputs,[],inputs,1) print("正在監聽的socket對象%d" % len(inputs)) print(r_list) for sk1_or_conn in r_list: if sk1_or_conn == sk1: conn,address = sk1_or_conn.accept() inputs.append(conn) message_dic[conn] = [] else: try: data_bytes = sk1_or_conn.recv(1024) data_str = str(data_bytes,encoding="utf-8") sk1_or_conn.sendall(bytes(data_str+"好",encoding="utf-8")) except Exception as ex: inputs.remove(sk1_or_conn) else: data_str = str(data_bytes,encoding="utf-8") message_dic[sk1_or_conn].append(data_str) outputs.append(sk1_or_conn) for conn in w_list: recv_str = message_dic[conn][0] del message_dic[conn][0] conn.sendall(bytes(recv_str+"好",encoding="utf-8")) for sk in e_list: inputs.remove(sk) 客戶端: import socket obj = socket.socket() obj.connect(('127.0.0.1',8001)) while True: inp = input("Please(q\退出):\n>>>") obj.sendall(bytes(inp,encoding="utf-8")) if inp == "q": break ret = str(obj.recv(1024),encoding="utf-8") print(ret)
SocketServer內部使用 IO多路複用 以及 「多線程」 和 「多進程」 ,從而實現併發處理多個客戶端請求的Socket服務端。即:每一個客戶端請求鏈接到服務器時,Socket服務端都會在服務器是建立一個「線程」或者「進程」 專門負責處理當前客戶端的全部請求。
ThreadingTCPServer
ThreadingTCPServer實現的Soket服務器內部會爲每一個client建立一個 「線程」,該線程用來和客戶端進行交互。
一、ThreadingTCPServer基礎
使用ThreadingTCPServer:
import socketserver class Myserver(socketserver.BaseRequestHandler): def handle(self): conn = self.request conn.sendall(bytes("你好,我是機器人",encoding="utf-8")) while True: ret_bytes = conn.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") if ret_str == "q": break conn.sendall(bytes(ret_str+"你好我好你們好",encoding="utf-8")) if __name__ == "__main__": server = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Myserver) server.serve_forever()
import socket obj = socket.socket() obj.connect(("127.0.0.1",8080)) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str) while True: inp = input("你好請問您有什麼問題? \n >>>") if inp == "q": obj.sendall(bytes(inp,encoding="utf-8")) break else: obj.sendall(bytes(inp, encoding="utf-8")) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding="utf-8") print(ret_str)
二、ThreadingTCPServer源碼剖析
ThreadingTCPServer的類圖關係以下:
內部調用流程爲:
相對應的源碼以下:
class BaseServer: """Base class for server classes. Methods for the caller: - __init__(server_address, RequestHandlerClass) - serve_forever(poll_interval=0.5) - shutdown() - handle_request() # if you do not use serve_forever() - fileno() -> int # for select() Methods that may be overridden: - server_bind() - server_activate() - get_request() -> request, client_address - handle_timeout() - verify_request(request, client_address) - server_close() - process_request(request, client_address) - shutdown_request(request) - close_request(request) - handle_error() Methods for derived classes: - finish_request(request, client_address) Class variables that may be overridden by derived classes or instances: - timeout - address_family - socket_type - allow_reuse_address Instance variables: - RequestHandlerClass - socket """ timeout = None def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False def server_activate(self): """Called by constructor to activate the server. May be overridden. """ pass def serve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: while not self.__shutdown_request: # XXX: Consider using another file descriptor or # connecting to the socket to wake this up instead of # polling. Polling reduces our responsiveness to a # shutdown request and wastes cpu at all other times. r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval) if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__is_shut_down.set() def shutdown(self): """Stops the serve_forever loop. Blocks until the loop has finished. This must be called while serve_forever() is running in another thread, or it will deadlock. """ self.__shutdown_request = True self.__is_shut_down.wait() # The distinction between handling, getting, processing and # finishing a request is fairly arbitrary. Remember: # # - handle_request() is the top-level call. It calls # select, get_request(), verify_request() and process_request() # - get_request() is different for stream or datagram sockets # - process_request() is the place that may fork a new process # or create a new thread to finish the request # - finish_request() instantiates the request handler class; # this constructor will handle the request all by itself def handle_request(self): """Handle one request, possibly blocking. Respects self.timeout. """ # Support people who used socket.settimeout() to escape # handle_request before self.timeout was available. timeout = self.socket.gettimeout() if timeout is None: timeout = self.timeout elif self.timeout is not None: timeout = min(timeout, self.timeout) fd_sets = _eintr_retry(select.select, [self], [], [], timeout) if not fd_sets[0]: self.handle_timeout() return self._handle_request_noblock() def _handle_request_noblock(self): """Handle one request, without blocking. I assume that select.select has returned that the socket is readable before this function was called, so there should be no risk of blocking in get_request(). """ try: request, client_address = self.get_request() except socket.error: return if self.verify_request(request, client_address): try: self.process_request(request, client_address) except: self.handle_error(request, client_address) self.shutdown_request(request) def handle_timeout(self): """Called if no new request arrives within self.timeout. Overridden by ForkingMixIn. """ pass def verify_request(self, request, client_address): """Verify the request. May be overridden. Return True if we should proceed with this request. """ return True def process_request(self, request, client_address): """Call finish_request. Overridden by ForkingMixIn and ThreadingMixIn. """ self.finish_request(request, client_address) self.shutdown_request(request) def server_close(self): """Called to clean-up the server. May be overridden. """ pass def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self) def shutdown_request(self, request): """Called to shutdown and close an individual request.""" self.close_request(request) def close_request(self, request): """Called to clean up an individual request.""" pass def handle_error(self, request, client_address): """Handle an error gracefully. May be overridden. The default is to print a traceback and continue. """ print '-'*40 print 'Exception happened during processing of request from', print client_address import traceback traceback.print_exc() # XXX But this goes to stderr! print '-'*40
class TCPServer(BaseServer): """Base class for various socket-based server classes. Defaults to synchronous IP stream (i.e., TCP). Methods for the caller: - __init__(server_address, RequestHandlerClass, bind_and_activate=True) - serve_forever(poll_interval=0.5) - shutdown() - handle_request() # if you don't use serve_forever() - fileno() -> int # for select() Methods that may be overridden: - server_bind() - server_activate() - get_request() -> request, client_address - handle_timeout() - verify_request(request, client_address) - process_request(request, client_address) - shutdown_request(request) - close_request(request) - handle_error() Methods for derived classes: - finish_request(request, client_address) Class variables that may be overridden by derived classes or instances: - timeout - address_family - socket_type - request_queue_size (only for stream sockets) - allow_reuse_address Instance variables: - server_address - RequestHandlerClass - socket """ address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = False def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): """Constructor. May be extended, do not override.""" BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise def server_bind(self): """Called by constructor to bind the socket. May be overridden. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() def server_activate(self): """Called by constructor to activate the server. May be overridden. """ self.socket.listen(self.request_queue_size) def server_close(self): """Called to clean-up the server. May be overridden. """ self.socket.close() def fileno(self): """Return socket file number. Interface required by select(). """ return self.socket.fileno() def get_request(self): """Get the request and client address from the socket. May be overridden. """ return self.socket.accept() def shutdown_request(self, request): """Called to shutdown and close an individual request.""" try: #explicitly shutdown. socket.close() merely releases #the socket and waits for GC to perform the actual close. request.shutdown(socket.SHUT_WR) except socket.error: pass #some platforms may raise ENOTCONN here self.close_request(request) def close_request(self, request): """Called to clean up an individual request.""" request.close()
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread. In addition, exception handling is done here. """ try: self.finish_request(request, client_address) self.shutdown_request(request) except: self.handle_error(request, client_address) self.shutdown_request(request) def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
class BaseRequestHandler: """Base class for request handler classes. This class is instantiated for each request to be handled. The constructor sets the instance variables request, client_address and server, and then calls the handle() method. To implement a specific service, all you need to do is to derive a class which defines a handle() method. The handle() method can find the request as self.request, the client address as self.client_address, and the server (in case it needs access to per-server information) as self.server. Since a separate instance is created for each request, the handle() method can define arbitrary other instance variariables. """ def __init__(self, request, client_address, server): self.request = request self.client_address = client_address self.server = server self.setup() try: self.handle() finally: self.finish() def setup(self): pass def handle(self): pass def finish(self): pass
SocketServer的ThreadingTCPServer之因此能夠同時處理請求得益於 select 和 Threading 兩個東西,其實本質上就是在服務器端爲每個客戶端建立一個線程,當前線程用來處理對應客戶端的請求,因此,能夠支持同時n個客戶端連接(長鏈接)。