-- address family:
- AF_INET{,6}: IP socket (default)
- AF_UNIX: Unix domain sockets
- others, 如 AF_DECNET (见
-- socket type:
- SOCK_STREAM (可靠连接 TCP)
- SOCK_DGRAM (UDP)
对于request-based servers:
-- client address在发出进一步的请求之前需要认证(这实际上把所有需要发出请求的进程在通过认证之前给阻塞住了)
-- 如何处理多请求:
- 同步 (一次只能处理一个请求)
- forking (fork一个新的进程来处理一个请求)
- threading (创建一个新的线程来处理一个请求)
在这个模块的各种类中,最简单的服务器类型就是synchronous TCP/IP server。这是一个糟糕的类设计,但是也保存了一些设计的类型理念。
+--------------+
| BaseServer |
+--------------+
|
v
+------------+ +----------------------+
| TCPServer |------->| UnixStreamServer |
+------------+ +----------------------+
|
v
+-------------+ +--------------------------+
| UDPServer |------->| UnixDatagramServer |
+-------------+ +--------------------------+
注意:UnixDatagramServer继承于UDPServer,而不是UnixStreamServer,IP和Unix stream server之间仅有的差异就是address family,两个服务器类的内容多数是简单的重复。
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
(详细可见后文示例)
例如,如果服务中包含请求修改的内存的状态,那么使用forking server没有任何意义(因为在子进程中修改将不对父进程的初始化状态有影响,父进程也不会把这个修改的参数传递给其他子进程)。这种情况下,你可以使用threading server,而且你更有可能需要用到“锁”,以此来避免两个请求同时到达而使服务器状态产生冲突。
此外,如果你在搭建如HTTP服务器等,所有的数据都会存储在外部(如文件系统中),当客户端的一项请求被处理时,并且客户端的读取数据的速度很慢,synchronous class将会使服务不做出响应,这可能需要维持很长时间。
在一些情况下,请求同步可能需要恰当的方法,但是为了在子进程中完成请求要受到请求数据的影响。这可以通过使用同步服务器来实现,并且在请求处理类中的Handle方法中明确指定fork的进程。
另一种处理多个同时发生的请求的方法是维系一张明确的完成请求的表单,使用select()方法来判定哪个请求应该在接下来做出响应(或者判断是否要处理新到来的请求),当每一个客户端需要建立很长时间的连接时,这对于stream services来说非常重要。(前提是不使用线程和子进程的方法)
<span style="color: #0000ff;">import</span><span style="color: #000000;"> socket </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> select </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> sys </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> os </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> errno </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> threading </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError: </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> dummy_threading as threading </span><span style="color: #800080;">__all__</span> = [<span style="color: #800000;">"</span><span style="color: #800000;">TCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingTCPServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ThreadingTCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">BaseRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">StreamRequestHandler</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">DatagramRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingMixIn</span><span style="color: #800000;">"</span>, <span style="color: #800000;">"</span><span style="color: #800000;">ForkingMixIn</span><span style="color: #800000;">"</span><span style="color: #000000;">] </span><span style="color: #0000ff;">if</span> hasattr(socket, <span style="color: #800000;">"</span><span style="color: #800000;">AF_UNIX</span><span style="color: #800000;">"</span><span style="color: #000000;">): </span><span style="color: #800080;">__all__</span>.extend([<span style="color: #800000;">"</span><span style="color: #800000;">UnixStreamServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UnixDatagramServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixStreamServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixDatagramServer</span><span style="color: #800000;">"</span>])
三、BaseServer和BaseRequestHandler
Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。在构建网络服务的时候,Server 和 RequestHandler 并不是分开的,RequestHandler的实例对象在Server 内配合 Server工作。
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request() # if you do not use serve_forever()
- fileno() -> int # for select()
timeout =<span style="color: #000000;"> None </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor. May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.server_address </span>=<span style="color: #000000;"> server_address self.RequestHandlerClass </span>=<span style="color: #000000;"> RequestHandlerClass self.</span><span style="color: #800080;">__is_shut_down</span> =<span style="color: #000000;"> threading.Event() self.</span><span style="color: #800080;">__shutdown_request</span> = False
<span style="color: #0000ff;">def</span> serve_forever(self, poll_interval=0.5<span style="color: #000000;">): </span><span style="color: #800000;">"""</span><span style="color: #800000;">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. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.</span><span style="color: #800080;">__is_shut_down</span><span style="color: #000000;">.clear() </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">while</span> <span style="color: #0000ff;">not</span> self.<span style="color: #800080;">__shutdown_request</span><span style="color: #000000;">: r, w, e </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [], poll_interval) </span><span style="color: #0000ff;">if</span> self <span style="color: #0000ff;">in</span><span style="color: #000000;"> r: self._handle_request_noblock() </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">: self.</span><span style="color: #800080;">__shutdown_request</span> =<span style="color: #000000;"> False self.</span><span style="color: #800080;">__is_shut_down</span>.set()
这里用到了select()函数,即server_forever接受了一个poll_interval=0.5的参数传入,这表示用于select轮询的时间,然后进入一个无限循环中,在这个循环中,select每隔poll_interval秒轮询一次(阻塞于此),以此来进行网络IO的监听。一旦有新的网络连接请求到来,则会调用_handle_request_noblock()方法处理新的连接。
3.1.3 _handle_request_noblock()
<span style="color: #0000ff;">def</span><span style="color: #000000;"> _handle_request_noblock(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">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(). </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">try</span><span style="color: #000000;">: request, client_address </span>=<span style="color: #000000;"> self.get_request() </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.verify_request(request, client_address): </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.process_request(request, client_address) </span><span style="color: #0000ff;">except</span><span style="color: #000000;">: self.handle_error(request, client_address) self.shutdown_request(request)</span>
英文说明已经说的很明确,该方法处理的是一个非阻塞请求,首先通过get_request()方法获取连接,具体实现在其子类,一旦获取了连接,立即调用verify_request认证连接信息,通过认证,则调用process_request()方法处理请求,如果中途出现错误,则调用handle_error处理错误,同时,调用shutdown_request()方法结束这个连接。
<span style="color: #0000ff;">def</span><span style="color: #000000;"> verify_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Verify the request. May be overridden. Return True if we should proceed with this request. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> True </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> process_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Call finish_request. Overridden by ForkingMixIn and ThreadingMixIn. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.finish_request(request, client_address) self.shutdown_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_error(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle an error gracefully. May be overridden. The default is to print a traceback and continue. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40 <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">Exception happened during processing of request from</span><span style="color: #800000;">'</span><span style="color: #000000;">, </span><span style="color: #0000ff;">print</span><span style="color: #000000;"> client_address </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> traceback traceback.print_exc() </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX But this goes to stderr!</span> <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40 <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.close_request(request)</span><pre name="<span" style="color: #800000;">"<span style="color: #800000;">code</span><span style="color: #800000;">"</span> <span style="color: #0000ff;">class</span>=<span style="color: #800000;">"</span><span style="color: #800000;">python</span><span style="color: #800000;">"</span>> <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Finish one request by instantiating RequestHandlerClass.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.RequestHandlerClass(request, client_address, self)</span>
verify_request()方法对request进行验证,通常会被子类重写。
3.1.4 handle_request()
<span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_request(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request, possibly blocking. Respects self.timeout. </span><span style="color: #800000;">"""</span> <span style="color: #008000;">#</span><span style="color: #008000;"> Support people who used socket.settimeout() to escape</span> <span style="color: #008000;">#</span><span style="color: #008000;"> handle_request before self.timeout was available.</span> timeout =<span style="color: #000000;"> self.socket.gettimeout() </span><span style="color: #0000ff;">if</span> timeout <span style="color: #0000ff;">is</span><span style="color: #000000;"> None: timeout </span>=<span style="color: #000000;"> self.timeout </span><span style="color: #0000ff;">elif</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None: timeout </span>=<span style="color: #000000;"> min(timeout, self.timeout) fd_sets </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [], timeout) </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> fd_sets[0]: self.handle_timeout() </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self._handle_request_noblock()</span>
上面已经提到,如果你没有用到server_forever()方法,说明你希望使用的是阻塞请求来处理连接,如英文描述所说,该方法只是处理一个阻塞的请求,仍然使用select()方法轮询来监听网络连接,但是需要考虑时间超时影响,一旦超时,调用handle_timeout()方法处理超时,一般在子类重写该方法;如果在超时之前监听到了网络的连接请求,则同server_forever一样,调用_handle_request_noblock()方法,完成对新的连接的请求处理。
3.2 BaseRequestHandler分析
<span style="color: #0000ff;">class</span><span style="color: #000000;"> BaseRequestHandler: </span><span style="color: #800000;">"""</span><span style="color: #800000;">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. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, request, client_address, server): self.request </span>=<span style="color: #000000;"> request self.client_address </span>=<span style="color: #000000;"> client_address self.server </span>=<span style="color: #000000;"> server self.setup() </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.handle() </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">: self.finish() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): </span><span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> handle(self): </span><span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): </span><span style="color: #0000ff;">pass</span>
以上描述说明,所有requestHandler都继承BaseRequestHandler基类,该类会处理每一个请求。在__init__中初始化实例变量request、client_address、server,然后调用handle()方法完成请求处理。那么,我们唯一需要做的就是重写好Handle()方法,处理所有的请求。
<span style="color: #0000ff;">class</span><span style="color: #000000;"> TCPServer(BaseServer): address_family </span>=<span style="color: #000000;"> socket.AF_INET socket_type </span>=<span style="color: #000000;"> socket.SOCK_STREAM request_queue_size </span>= 5<span style="color: #000000;"> allow_reuse_address </span>=<span style="color: #000000;"> False </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span>(self, server_address, RequestHandlerClass, bind_and_activate=<span style="color: #000000;">True): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor. May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> BaseServer.</span><span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass) self.socket </span>=<span style="color: #000000;"> socket.socket(self.address_family, self.socket_type) </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> bind_and_activate: </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.server_bind() self.server_activate() </span><span style="color: #0000ff;">except</span><span style="color: #000000;">: self.server_close() </span><span style="color: #0000ff;">raise</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> server_bind(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to bind the socket. May be overridden. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, </span>1<span style="color: #000000;">) self.socket.bind(self.server_address) self.server_address </span>=<span style="color: #000000;"> self.socket.getsockname() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to activate the server. May be overridden. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.socket.listen(self.request_queue_size) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_close(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean-up the server. May be overridden. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.socket.close() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> fileno(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Return socket file number. Interface required by select(). </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.fileno() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Get the request and client address from the socket. May be overridden. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.accept() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #008000;">#</span><span style="color: #008000;">explicitly shutdown. socket.close() merely releases</span> <span style="color: #008000;">#</span><span style="color: #008000;">the socket and waits for GC to perform the actual close.</span> <span style="color: #000000;"> request.shutdown(socket.SHUT_WR) </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #0000ff;">pass</span> <span style="color: #008000;">#</span><span style="color: #008000;">some platforms may raise ENOTCONN here</span> <span style="color: #000000;"> self.close_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean up an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> request.close()</span>
在BaseServer基础上增加了一个TCP的socket连接,使用server_bind、server_activate、server_close处理TCP启停等操作,同时增加了get_request、shutdown_request、close_request处理客户端请求。
<span style="color: #0000ff;">class</span><span style="color: #000000;"> UDPServer(TCPServer): </span><span style="color: #800000;">"""</span><span style="color: #800000;">UDP server class.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> allow_reuse_address </span>=<span style="color: #000000;"> False socket_type </span>=<span style="color: #000000;"> socket.SOCK_DGRAM max_packet_size </span>= 8192 <span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self): data, client_addr </span>=<span style="color: #000000;"> self.socket.recvfrom(self.max_packet_size) </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (data, self.socket), client_addr </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to call listen() for UDP.</span> <span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to shutdown anything.</span> <span style="color: #000000;"> self.close_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to close anything.</span> <span style="color: #0000ff;">pass</span>
继承自TCPServer,将socket改为了SOCK_DGRAM型,并修改了get_request,用于从SOCK_DGRAM中获取request。同时server_activate、shutdown_request、close_request都改成了空(UDP不需要),比TCP简单一些。
<span style="color: #0000ff;">class</span><span style="color: #000000;"> StreamRequestHandler(BaseRequestHandler): rbufsize </span>= -1<span style="color: #000000;"> wbufsize </span>=<span style="color: #000000;"> 0 timeout </span>=<span style="color: #000000;"> None disable_nagle_algorithm </span>=<span style="color: #000000;"> False </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): self.connection </span>=<span style="color: #000000;"> self.request </span><span style="color: #0000ff;">if</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None: self.connection.settimeout(self.timeout) </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> self.disable_nagle_algorithm: self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) self.rfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">rb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.rbufsize) self.wfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">wb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.wbufsize) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.wfile.closed: </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.wfile.flush() </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #008000;">#</span><span style="color: #008000;"> An final socket error may have occurred here, such as</span> <span style="color: #008000;">#</span><span style="color: #008000;"> the local error ECONNABORTED.</span> <span style="color: #0000ff;">pass</span><span style="color: #000000;"> self.wfile.close() self.rfile.close()</span>
最主要的功能是根据socket生成了读写socket用的两个文件对象(可以理解为句柄)rfile和wfile
<span style="color: #0000ff;">class</span><span style="color: #000000;"> DatagramRequestHandler(BaseRequestHandler): </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX Regrettably, I cannot get this working on Linux;</span> <span style="color: #008000;">#</span><span style="color: #008000;"> s.recvfrom() doesn't return a meaningful client address.</span> <span style="color: #800000;">"""</span><span style="color: #800000;">Define self.rfile and self.wfile for datagram sockets.</span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">from</span> cStringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError: </span><span style="color: #0000ff;">from</span> StringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO self.packet, self.socket </span>=<span style="color: #000000;"> self.request self.rfile </span>=<span style="color: #000000;"> StringIO(self.packet) self.wfile </span>=<span style="color: #000000;"> StringIO() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): self.socket.sendto(self.wfile.getvalue(), self.client_address)</span>
同样是生成rfile和wfile,但UDP不直接关联socket。这里的rfile是直接由从UDP中读取的数据生成的,wfile则是新建了一个StringIO,用于写数据。
(题目起的有点大,部分剖析的不好,等之后再往祖坟上刨。。。。^-^)
参考博客:http://www.cnblogs.com/tuzkee/p/3573210.html

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

DVWA
Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

Muat turun versi mac editor Atom
Editor sumber terbuka yang paling popular

Dreamweaver Mac版
Alat pembangunan web visual

PhpStorm versi Mac
Alat pembangunan bersepadu PHP profesional terkini (2018.2.1).

SecLists
SecLists ialah rakan penguji keselamatan muktamad. Ia ialah koleksi pelbagai jenis senarai yang kerap digunakan semasa penilaian keselamatan, semuanya di satu tempat. SecLists membantu menjadikan ujian keselamatan lebih cekap dan produktif dengan menyediakan semua senarai yang mungkin diperlukan oleh penguji keselamatan dengan mudah. Jenis senarai termasuk nama pengguna, kata laluan, URL, muatan kabur, corak data sensitif, cangkerang web dan banyak lagi. Penguji hanya boleh menarik repositori ini ke mesin ujian baharu dan dia akan mempunyai akses kepada setiap jenis senarai yang dia perlukan.
