与前一例不同,本次考虑多个客户端连接服务器的情况,且可以异步通信。
服务器不需要在阻塞模式中处理客户发出的请求,而是单独处理每个请求。如果某个客户端接受或处理数据花了很长时间,服务器无需等待处理完成,即可使用另外的线程和其他客户端进行通信。
实现方法
- SocketServer模块:利用SocketServer模块提供的类可以直接实现TCP、UDP及其他协议服务器。
- ForkingMixIn类,用于异步处理客户端。
- 我们创建ForkingServer类,继承TCPServer和ForkingMixIn类。继承TCPServer可以省去手动创建服务器操作,如创建套接字、绑定地址和监听连接等;继承ForkingMinIn用于异步处理。
- ForkingServer类还要创建一个请求处理程序ForkingServerRequestHandler(继承自SocketServer库中的BaseRequestHandler类),说明如何处理客户端请求。
- 客户端ForkingClient使用面向对象的方式编写。
代码 2_1_forking_mixin_socket_server.py
'''
Created on 2017-2-28
@author: lenovo
'''
import os
import socket
import threading
import SocketServer
SERVER_HOST = 'localhost'
SERVER_PORT = 0 # Tells the kernel to pick up a port dynamically
BUF_SIZE = 1024
ECHO_MSG = 'Hello echo server!'
class ForkingClient():
""" A client to test forking server"""
def __init__(self,ip,port):
# Create a socket
self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.sock.connect((ip,port))
def run(self):
"""Client playing with the server"""
#send the data to server
current_process_id = os.getpid()
print 'PID %s Sending echo message to the server : "%s"' %(current_process_id,ECHO_MSG)
sent_data_length = self.sock.send(ECHO_MSG)
print "Sent: %d characters, so far..." %sent_data_length
# Display server response
response = self.sock.recv(BUF_SIZE)
print "PID %s received: %s" %(current_process_id,response[5:])
def shutdown(self):
self.sock.close()
class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
# Send the echo back to the client
data = self.request.recv(BUF_SIZE)
current_process_id = os.getpid()
response = '%s: %s' % (current_process_id,data)
print "Server sending response [current_process_id: data] = [%s]" %response
self.request.send(response)
return
class ForkingServer(SocketServer.ForkingMixIn,SocketServer.TCPServer,):
"""Nothing to add here, inherited everything necessary form parents."""
pass
def main():
#Launch the server
server = ForkingServer((SERVER_HOST,SERVER_PORT),ForkingServerRequestHandler)
ip,port = server.server_address #Retrive the port number
server_thread = threading.Thread(target=server.serve_forever)
server_thread.setDaemon(True)
server_thread.start()
print 'Server loop running PID: %s' %os.getpid()
#Launch the client
client1 = ForkingClient(ip,port)
client1.run()
client2 = ForkingClient(ip,port)
client2.run()
#clean them up
server.shutdown()
client1.shutdown()
client2.shutdown()
server.socket.close()
if __name__ == '__main__':
main()
运行结果
xx@ubuntu:~$ python 2_1_forking_mixin_socket_server.py
Server loop running PID: 2545
PID 2545 Sending echo message to the server : "Hello echo server!"
Sent: 18 characters, so far...
Server sending response [current_process_id: data] = [2547: Hello echo server!]
PID 2545 received: Hello echo server!
PID 2545 Sending echo message to the server : "Hello echo server!"
Sent: 18 characters, so far...
Server sending response [current_process_id: data] = [2548: Hello echo server!]
PID 2545 received: Hello echo server!
xx@ubuntu:~$
原理分析
主线程中创建了一个ForkingServer实例,作为守护进程在后台运行,然后再创建两个客户端与服务器交互。
不难发现,服务器每次响应请求的进程是不同,一个是2547,一个是2548,可见服务器对每个客户端请求生成了不同的进程去去处理。