Python學習筆記_基礎篇(十)_socket編程

本章內容

1、socket

2、IO多路復用

3、socketserver

Socket

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的區別:

1、file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】

2、socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】

那我們就先來創建一個socket服務端吧

import socketsk = socket.socket()
sk.bind(("127.0.0.1",8080))
sk.listen(5)conn,address = sk.accept()
sk.sendall(bytes("Hello world",encoding="utf-8"))

server

import socketobj = socket.socket()
obj.connect(("127.0.0.1",8080))ret = str(obj.recv(1024),encoding="utf-8")
print(ret)

View Code

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 apair (host, port); the host must refer to the local host. For raw packetsockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"""
'''將套接字綁定到本地地址。是一個IP套接字的地址對(主機、端口),主機必須參考本地主機。'''passdef close(self): # real signature unknown; restored from __doc__"""close()Close the socket.  It cannot be used after this call."""'''關閉socket'''passdef connect(self, address): # real signature unknown; restored from __doc__"""connect(address)Connect the socket to a remote address.  For IP sockets, the addressis a pair (host, port)."""'''將套接字連接到遠程地址。IP套接字的地址'''passdef connect_ex(self, address): # real signature unknown; restored from __doc__"""connect_ex(address) -> errnoThis is like connect(address), but returns an error code (the errno value)instead of raising an exception when an error occurs."""passdef 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 descriptorcan be reused for other purposes.  The file descriptor is returned."""
'''關閉套接字對象沒有關閉底層的文件描述符。'''passdef fileno(self): # real signature unknown; restored from __doc__"""fileno() -> integerReturn the integer file descriptor of the socket."""'''返回整數的套接字的文件描述符。'''return 0def getpeername(self): # real signature unknown; restored from __doc__"""getpeername() -> address infoReturn the address of the remote endpoint.  For IP sockets, the addressinfo is a pair (hostaddr, port)."""'''返回遠程端點的地址。IP套接字的地址'''passdef getsockname(self): # real signature unknown; restored from __doc__"""getsockname() -> address infoReturn the address of the local endpoint.  For IP sockets, the addressinfo is a pair (hostaddr, port)."""'''返回遠程端點的地址。IP套接字的地址'''passdef getsockopt(self, level, option, buffersize=None): # real signature unknown; restored from __doc__"""getsockopt(level, option[, buffersize]) -> valueGet a socket option.  See the Unix manual for level and option.If a nonzero buffersize argument is given, the return value is astring of that length; otherwise it is an integer."""'''得到一個套接字選項'''passdef gettimeout(self): # real signature unknown; restored from __doc__"""gettimeout() -> timeoutReturns the timeout in seconds (float) associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled."""'''返回的超時秒數(浮動)與套接字相關聯'''return timeoutdef ioctl(self, cmd, option): # real signature unknown; restored from __doc__"""ioctl(cmd, option) -> longControl the socket with WSAIoctl syscall. Currently supported 'cmd' values areSIO_RCVALL:  'option' must be one of the socket.RCVALL_* constants.SIO_KEEPALIVE_VALS:  'option' is a tuple of (onoff, timeout, interval)."""return 0def listen(self, backlog=None): # real signature unknown; restored from __doc__"""listen([backlog])Enable a server to accept connections.  If backlog is specified, it must beat least 0 (if it is lower, it is set to 0); it specifies the number ofunaccepted connections that the system will allow before refusing newconnections. If not specified, a default reasonable value is chosen."""'''使服務器能夠接受連接。'''passdef recv(self, buffersize, flags=None): # real signature unknown; restored from __doc__"""recv(buffersize[, flags]) -> dataReceive up to buffersize bytes from the socket.  For the optional flagsargument, see the Unix manual.  When no data is available, block untilat least one byte is available or until the remote end is closed.  Whenthe remote end is closed and all data is read, return the empty string."""
'''當沒有數據可用,阻塞,直到至少一個字節是可用的或遠程結束之前關閉。'''passdef 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."""passdef 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."""passdef recv_into(self, buffer, nbytes=None, flags=None): # real signature unknown; restored from __doc__"""recv_into(buffer, [nbytes[, flags]]) -> nbytes_readA 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."""passdef send(self, data, flags=None): # real signature unknown; restored from __doc__"""send(data[, flags]) -> countSend a data string to the socket.  For the optional flagsargument, see the Unix manual.  Return the number of bytessent; this may be less than len(data) if the network is busy."""'''發送一個數據字符串到套接字。'''passdef sendall(self, data, flags=None): # real signature unknown; restored from __doc__"""sendall(data[, flags])Send a data string to the socket.  For the optional flagsargument, see the Unix manual.  This calls send() repeatedlyuntil all data is sent.  If an error occurs, it's impossibleto tell how much data has been sent."""'''發送一個數據字符串到套接字,直到所有數據發送完成'''passdef sendto(self, data, flags=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ """sendto(data[, flags], address) -> countLike send(data, flags) but allows specifying the destination address.For IP sockets, the address is a pair (hostaddr, port)."""passdef 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時一旦無數據,則報錯。'''passdef 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."""passdef 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 disablesthe timeout feature and is equivalent to setblocking(1).Setting a timeout of zero is the same as setblocking(0)."""passdef share(self, process_id): # real signature unknown; restored from __doc__"""share(process_id) -> bytesShare the socket with another process.  The target process idmust be provided and the resulting bytes object passed to the targetprocess.  There the shared socket can be instantiated by callingsocket.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 sideof the socket (flag == SHUT_WR), or both ends (flag == SHUT_RDWR)."""passdef _accept(self): # real signature unknown; restored from __doc__"""_accept() -> (integer, address info)Wait for an incoming connection.  Return a new socket file descriptorrepresenting 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.requestconn.sendall(bytes("你好,我是機器人",encoding="utf-8"))while True:ret_bytes = conn.recv(1024)ret_str = str(ret_bytes,encoding="utf-8")if ret_str == "q":breakconn.sendall(bytes(ret_str+"你好我好大家好",encoding="utf-8"))if __name__ == "__main__":server = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Myserver)server.serve_forever()客戶端import socketobj = 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"))breakelse:obj.sendall(bytes(inp, encoding="utf-8"))ret_bytes = obj.recv(1024)ret_str = str(ret_bytes,encoding="utf-8")print(ret_str)

案例一 機器人聊天

服務端import socketsk = 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 = 0f = open("db_new.jpg","wb")while True:if file_size == has_size:breakdate = conn.recv(1024)f.write(date)has_size += len(date)f.close()客戶端import socket
import osobj = 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 dataimport 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':breaksk.sendto(bytes(inp,encoding = "utf-8"),ip_port)sk.close()

udp傳輸

WEB服務應用:

#!/usr/bin/env python
#coding:utf-8
import socketdef 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()

IO多路復用

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:提供: selectMac Python:提供: selectLinux Python:提供: select、poll、epoll

對于select模塊操作的方法:

句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超時時間)參數: 可接受四個參數(前三個必須)
返回值:三個列表select方法用來監視文件句柄,如果句柄發生變化,則獲取該句柄。
1、當 參數1 序列中的句柄發生可讀時(accetp和read),則獲取發生變化的句柄并添加到 返回值1 序列中
2、當 參數2 序列中含有句柄時,則將該序列中所有的句柄添加到 返回值2 序列中
3、當 參數3 序列中的句柄發生錯誤時,則將該發生錯誤的句柄添加到 返回值3 序列中
4、當 超時時間 未設置,則select會一直阻塞,直到監聽的句柄發生變化
5、當 超時時間 = 1時,那么如果監聽的句柄均無任何變化,則select會阻塞 1 秒,之后返回三個空列表,如果監聽的句柄有變化,則直接執行。

import socket
import selectsk1 = 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"))

利用select監聽終端操作實例

服務端:
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 socketobj = 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":breakret = str(obj.recv(1024),encoding="utf-8")print(ret)

利用select實現偽同時處理多個Socket客戶端請求

服務端:
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 socketobj = 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":breakret = str(obj.recv(1024),encoding="utf-8")print(ret)

利用select實現偽同時處理多個Socket客戶端請求讀寫分離

socketserver

SocketServer內部使用 IO多路復用 以及 “多線程” 和 “多進程” ,從而實現并發處理多個客戶端請求的Socket服務端。即:每個客戶端請求連接到服務器時,Socket服務端都會在服務器是創建一個“線程”或者“進程” 專門負責處理當前客戶端的所有請求。

ThreadingTCPServer

ThreadingTCPServer實現的Soket服務器內部會為每個client創建一個 “ 線程 ”,該線程用來和客戶端進行交互。

1、ThreadingTCPServer基礎

使用ThreadingTCPServer:

  • 創建一個繼承自 SocketServer.BaseRequestHandler 的類
  • 類中必須定義一個名稱為 handle 的方法
  • 啟動ThreadingTCPServer

import  socketserverclass Myserver(socketserver.BaseRequestHandler):def handle(self):conn = self.requestconn.sendall(bytes("你好,我是機器人",encoding="utf-8"))while True:ret_bytes = conn.recv(1024)ret_str = str(ret_bytes,encoding="utf-8")if ret_str == "q":breakconn.sendall(bytes(ret_str+"你好我好大家好",encoding="utf-8"))if __name__ == "__main__":server = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Myserver)server.serve_forever()

服務端

import socketobj = 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"))breakelse:obj.sendall(bytes(inp, encoding="utf-8"))ret_bytes = obj.recv(1024)ret_str = str(ret_bytes,encoding="utf-8")print(ret_str)

客戶端

2、ThreadingTCPServer源碼剖析

ThreadingTCPServer的類圖關系如下:

內部調用流程為:

  • 啟動服務端程序
  • 執行 TCPServer.init 方法,創建服務端Socket對象并綁定 IP 和 端口
  • 執行 BaseServer.init 方法,將自定義的繼承自SocketServer.BaseRequestHandler 的類 MyRequestHandle賦值給 self.RequestHandlerClass
  • 執行 BaseServer.server_forever 方法,While 循環一直監聽是否有客戶端請求到達 …
  • 當客戶端連接到達服務器
  • 執行 ThreadingMixIn.process_request 方法,創建一個 “線程” 用來處理請求
  • 執行 ThreadingMixIn.process_request_thread 方法
  • 執行 BaseServer.finish_request 方法,執行 self.RequestHandlerClass() 即:執行 自定義 MyRequestHandler 的構造方法(自動調用基類BaseRequestHandler的構造方法,在該構造方法中又會調用 MyRequestHandler的handle方法)

相對應的源碼如下:

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 orinstances:- timeout- address_family- socket_type- allow_reuse_addressInstance variables:- RequestHandlerClass- socket"""timeout = Nonedef __init__(self, server_address, RequestHandlerClass):"""Constructor.  May be extended, do not override."""self.server_address = server_addressself.RequestHandlerClass = RequestHandlerClassself.__is_shut_down = threading.Event()self.__shutdown_request = Falsedef server_activate(self):"""Called by constructor to activate the server.May be overridden."""passdef serve_forever(self, poll_interval=0.5):"""Handle one request at a time until shutdown.Polls for shutdown every poll_interval seconds. Ignoresself.timeout. If you need to do periodic tasks, do them inanother 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 = Falseself.__is_shut_down.set()def shutdown(self):"""Stops the serve_forever loop.Blocks until the loop has finished. This must be called whileserve_forever() is running in another thread, or it willdeadlock."""self.__shutdown_request = Trueself.__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 itselfdef 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.timeoutelif 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()returnself._handle_request_noblock()def _handle_request_noblock(self):"""Handle one request, without blocking.I assume that select.select has returned that the socket isreadable before this function was called, so there should beno risk of blocking in get_request()."""try:request, client_address = self.get_request()except socket.error:returnif 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."""passdef verify_request(self, request, client_address):"""Verify the request.  May be overridden.Return True if we should proceed with this request."""return Truedef 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."""passdef 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."""passdef handle_error(self, request, client_address):"""Handle an error gracefully.  May be overridden.The default is to print a traceback and continue."""print '-'*40print 'Exception happened during processing of request from',print client_addressimport tracebacktraceback.print_exc() # XXX But this goes to stderr!print '-'*40

Baseserver

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 orinstances:- timeout- address_family- socket_type- request_queue_size (only for stream sockets)- allow_reuse_addressInstance variables:- server_address- RequestHandlerClass- socket"""address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMrequest_queue_size = 5allow_reuse_address = Falsedef __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()raisedef 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 hereself.close_request(request)def close_request(self, request):"""Called to clean up an individual request."""request.close()

TCP server

class ThreadingMixIn:"""Mix-in class to handle each request in a new thread."""# Decides how threads will act upon termination of the# main processdaemon_threads = Falsedef 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_threadst.start()

ThreadingMixIn

class BaseRequestHandler:"""Base class for request handler classes.This class is instantiated for each request to be handled.  Theconstructor sets the instance variables request, client_addressand server, and then calls the handle() method.  To implement aspecific service, all you need to do is to derive a class whichdefines a handle() method.The handle() method can find the request as self.request, theclient address as self.client_address, and the server (in case itneeds access to per-server information) as self.server.  Since aseparate instance is created for each request, the handle() methodcan define arbitrary other instance variariables."""def __init__(self, request, client_address, server):self.request = requestself.client_address = client_addressself.server = serverself.setup()try:self.handle()finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):pass

SocketServer.BaseRequestHandler

SocketServer的ThreadingTCPServer之所以可以同時處理請求得益于 selectThreading 兩個東西,其實本質上就是在服務器端為每一個客戶端創建一個線程,當前線程用來處理對應客戶端的請求,所以,可以支持同時n個客戶端鏈接(長連接)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39955.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39955.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39955.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

spring boot 簡單整合 Redis

1.添加依賴<!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- commons-pool2 --><dependency><groupId>org.ap…

Linux安裝Docker

一、Docker系統版本介紹 Docker 是一個開源的應用容器引擎&#xff0c;讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中&#xff0c;然后發布到任何流行的 Linux 或 Windows 操作系統的機器上&#xff0c;也可以實現虛擬化。 容器是完全使用沙箱機制&#xff0c;相…

誠邁科技榮膺小米“最佳供應商獎”

近日&#xff0c;誠邁科技受邀參加小米戰略合作伙伴HBR總結會。誠邁科技以盡職盡責的合作態度、精益求精的交付質量榮膺小米公司頒發的最佳供應商獎&#xff0c;其性能測試團隊榮獲優秀團隊獎。 誠邁科技與小米在手機終端方向一直保持著密切的合作關系&#xff0c;涉及系統框架…

centOS 快速安裝和配置 NVIDIA docker Container Toolkit

要在 CentOS 上正確安裝和配置 NVIDIA Container Toolkit&#xff0c;您可以按照以下步驟進行操作&#xff0c;如果1和2都已經完成&#xff0c;可以直接進行第3步NVIDIA Container Toolkit安裝配置。 1. 安裝 NVIDIA GPU 驅動程序&#xff1a; 您可以從 NVIDIA 官方網站下載適…

【Java基礎】Java對象的生命周期

【Java基礎】Java對象的生命周期 一、概述 一個類通過編譯器將一個Java文件編譯為Class字節碼文件&#xff0c;然后通過JVM中的解釋器編譯成不同操作系統的機器碼。雖然操作系統不同&#xff0c;但是基于解釋器的虛擬機是相同的。java類的生命周期就是指一個class文件加載到類…

Ubuntu安裝MySQL Server提示Depends: mysql-server-5.5怎么解決

在 Ubuntu 安裝 MySQL Server 時出現 Depends: mysql-server-5.5 的錯誤通常是因為系統中沒有找到所需的軟件包版本。這可能是因為軟件包源中沒有對應的版本或者軟件包版本沖突等原因。解決這個問題的方法如下&#xff1a; 更新軟件包列表&#xff1a; 在終端中運行以下命令&a…

python控制obs實現無縫切換場景!obs-websocket-py

前言 最近一直在研究孿生數字人wav2lip。目前成果可直接輸入高清嘴型&#xff0c;2070顯卡1分鐘音頻2.6分鐘輸出。在直播邏輯上可以做到1比1.3這樣&#xff0c;所以現在開始研究直播。在邏輯上涉及到了無縫切換&#xff0c;看到csdn上有一篇文章還要vip解鎖。。。那自己研究吧…

臨時用工小程序:一款便捷的用工管理軟件

隨著企業對人力資源需求的不斷增長&#xff0c;臨時用工需求也日益旺盛。為了滿足這一需求&#xff0c;我們研發了一款名為“臨時用工小程序”的軟件系統&#xff0c;旨在幫助企業實現臨時用工的高效管理。 一、技術棧介紹 后端技術棧 本系統采用Java語言作為開發語言&#…

尚硅谷MySQL筆記 3-9

我不會記錄的特別詳細 大體框架 基本的Select語句運算符排序與分頁多表查詢單行函數聚合函數子查詢 第三章 基本的SELECT語句 SQL分類 這個分類有很多種&#xff0c;大致了解下即可 DDL&#xff08;Data Definition Languages、數據定義語言&#xff09;&#xff0c;定義了…

項目難點:解決IOS調用起軟鍵盤之后頁面樣式布局錯亂問題

需求背景 &#xff1a; 開發了一個問卷系統重構項目&#xff0c;剛開始開發的為 PC 端&#xff0c;其中最頭疼的一點無非就是 IE 瀏覽器的兼容適配性問題&#xff1b; 再之后項目經理要求開發移動端&#xff0c;簡單的說就是寫 H5 頁面&#xff0c;到時候會內嵌在 App 應用、辦…

multiple definition of......first defined here

一、背景 環境&#xff1a; 銀河麒麟–ARM–GCC7.4.0 寫了一個動態庫&#xff0c;依賴opencv和freeImage等第三方庫&#xff0c;用cmake進行編譯。原本在centos6-x86-gcc7.5.0上面進行編譯非常的順利&#xff0c;但是拿到麒麟arm上面編譯就提示了這個錯誤&#xff1a;這個報錯…

Python conda命令

Windows下 Anaconda Prompt 這個東西就是用來管理Anaconda的&#xff0c;使用的是conda這樣的一種命令 在Linux中&#xff0c;可以直接在終端中輸入conda 命令 可以使用conda命令創建新的python環境&#xff08;python版本&#xff0c;包&#xff09;&#xff0c;新的環境與原…

Ruby軟件外包開發語言特點

Ruby 是一種動態、開放源代碼的編程語言&#xff0c;它注重簡潔性和開發人員的幸福感。在許多方面都具有優點&#xff0c;但由于其動態類型和解釋執行的特性&#xff0c;它可能不適合某些對性能和類型安全性要求較高的場景。下面和大家分享 Ruby 語言的一些主要特點以及適用的場…

【C語言】動態通訊錄 -- 詳解

?前言 前面詳細介紹了靜態版通訊錄【C語言】靜態通訊錄 -- 詳解_炫酷的伊莉娜的博客-CSDN博客&#xff0c;但是靜態版通訊錄的空間是無法被改變的&#xff0c;而且空間利用率也不高。為了解決靜態通訊錄這一缺點&#xff0c;這時就要有一個能夠隨著存入聯系人數量的增加而增大…

Ansys Zemax | 手機鏡頭設計 - 第 1 部分:光學設計

本文是 3 篇系列文章的一部分&#xff0c;該系列文章將討論智能手機鏡頭模組設計的挑戰&#xff0c;從概念、設計到制造和結構變形的分析。本文是三部分系列的第一部分&#xff0c;將專注于OpticStudio中鏡頭模組的設計、分析和可制造性評估。&#xff08;聯系我們獲取文章附件…

Vue緩存路由組件

目錄 一、使用 一、使用 作用&#xff1a;讓不展示的路由組件保持掛載&#xff0c;不被銷毀 <template><div><h2>Home組件內容</h2><div><ul class"nav nav-tabs"><li><router-link class"list-group-item"…

安防監控視頻云存儲平臺EasyNVR通道頻繁離線的原因排查與解決

安防視頻監控匯聚EasyNVR視頻集中存儲平臺&#xff0c;是基于RTSP/Onvif協議的安防視頻平臺&#xff0c;可支持將接入的視頻流進行全平臺、全終端分發&#xff0c;分發的視頻流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。為了滿足用戶的集成與二次開發需求&#xf…

OpenCV(二)——圖像基本處理(二)

目錄 2.圖像的幾何變換 2.1 圖像平移 2.2 圖像縮放 2.3 圖像旋轉 2.4 仿射變換 2.5 透視變換

企業計算機服務器遭到了locked勒索病毒攻擊如何解決,勒索病毒解密

網絡技術的不斷發展&#xff0c;也為網絡安全埋下了隱患&#xff0c;近期&#xff0c;我們收到很多企業的求助&#xff0c;企業的計算機服務器遭到了locked勒索病毒的攻擊&#xff0c;導致企業的財務系統內的所有數據被加密無法讀取&#xff0c;嚴重影響了企業的正常運行。最近…

如何通過觀測云的RUM找到前端加載的瓶頸--可觀測性入門篇

聲明與保證 本文寫作于2023年6月&#xff0c;性能優化的評價標準和優化方式僅適用于當前觀測云控制臺&#xff0c;當然隨著產品迭代及技術更新&#xff0c;本文也會應要求適當更新。 創建、修訂時間創建修改人版本2023/6/24觀測云***v1.0.0 1.網站性能評價的發展史&#xff…