Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

Python网络编程(2)--使用多路复用套接字I/O 提升性能

$
0
0

本文专注于使用一些有用的技术提升套接字服务器的性能。和前一篇不同,本文考虑多个客户端连接服务器的情况,而且可以异步通信。服务器不需要在阻塞模式中处理客户端发出的请求,而是单独处理每个请求。如果某个客户端接收或处理数据时花了很长时间,服务器无需等待处理完成,可以使用另外的线程或进程和其他客户端通信。

本文还要介绍select模块。这个模块建立在底层操作系统内核的select系统调用基础之上,提供了平台专用的I/O监控功能。 linux用户可访问 http://man7.org/linux/man-pages/man2/select.2.html 查看手册,手册中介绍了select系统调用的可用功能。我们的套接字服务器要和多个客户端交互,所以select可以帮助我们监控非阻塞式套接字。

1、在套接字服务器程序中使用 ForkingMixIn

python 2.7版中的SocketServer模块提供了两个实用类: ForkingMixIn和ThreadingMixIn。ForkingMixIn会为每个客户端请求派生一个新进程。 有关SocketServer模块的详情,请参阅Python文档: http://docs.python.org/2/library/socketserver.html 。

#coding=utf-8
import os
import socket
import threading
import SocketServer
SERVER_HOST = 'localhost'
SERVER_PORT = 0
BUF_SIZE = 1024
ECHO_MSG = 'Hello echo server'
class ForkingClient():
"""一个测试的客户端"""
def __init__(self, ip, port):
# 创建一个套接字
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到服务器
self.sock.connect((ip, port))
def run(self):
"""客户端与服务器交互"""
# 发送数据到服务器
current_process_id = os.getpid()
print 'PID %s Sending echo message to the server: %s' % (current_process_id, ECHO_MSG)
send_data_length = self.sock.send(ECHO_MSG)
print "Send: %d characters, so far..." % send_data_length
# 显示服务器响应
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):
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,):
"""此处没有代码,全部继承自父类"""
pass
def main():
# 启动服务器
server = ForkingServer((SERVER_HOST, SERVER_PORT), ForkingServerRequestHandler)
ip, port = server.server_address
server_thread = threading.Thread(target=server.serve_forever)
server_thread.setDaemon(True)
server_thread.start()
print 'Server loop running PID: %s' %os.getpid()
# 启动客户端们
client1 = ForkingClient(ip, port)
client1.run()
client2 = ForkingClient(ip, port)
client2.run()
# 清理
server.shutdown()
client1.shutdown()
client2.shutdown()
server.socket.close()
if __name__ == '__main__':
main()

主线程中创建了一个ForkingServer实例,作为守护进程在后台运行。然后再创建两个客户端和服务器交互。

运行代码:

wu@ubuntu:~/python$ python 2_1_forking_mixin_socket_server.py

Server loop running PID: 16966

PID 16966 Sending echo message to the server: Hello echo server

Send: 17 characters, so far...

Server sending response [current_process_id:data] = [16968: Hello echo server]

PID 16966 received: : Hello echo server

PID 16966 Sending echo message to the server: Hello echo server

Send: 17 characters, so far...

Server sending response [current_process_id:data] = [16969: Hello echo server]

PID 16966 received: : Hello echo server

2、在套接字服务器程序中使用 ThreadingMixIn

或许基于某些原因你不想编写基于进程的应用程序,而更愿意编写多线程应用程序。可能的原因有:在线程之间共享应用的状态,避免进程间通信的复杂操作,等等。遇到这种需求,如果想使用SocketServer库编写异步网络服务器,就得使用ThreadingMixIn类

和上文中基于ForkingMixIn的套接字服务器一样,使用ThreadingMixIn编写的套接字服务器要遵循相同的回显服务器编程模式,不过仍有几点不同。首先, ThreadedTCPServer继承自TCPServer和TheadingMixIn。客户端连接这个多线程版服务器时,会创建一个新线程。详情参见 http://docs.python.org/2/library/socketserver.html 。

套接字服务器的请求处理类ForkingServerRequestHandler在一个新线程中把消息回显给客户端。在这个类中可以获取线程的信息。简单起见,我们把客户端的代码放在一个函数中,而不是一个类中。客户端代码创建客户端套接字,然后向服务器发送消息。

#coding=utf-8
import os
import socket
import threading
import SocketServer
SERVER_HOST = 'localhost'
SERVER_PORT = 0 #告诉内核动态收集端口
BUF_SIZE = 1024
def client(ip, port, message):
"""测试mixin服务端的客户端"""
# 连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
try:
sock.sendall(message)
response = sock.recv(BUF_SIZE)
print "Client received: %s" %response
finally:
sock.close()
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
"""一个基于TCP线程请求事务的例子"""
def handle(self):
data = self.request.recv(1024)
current_thread = threading.current_thread()
response = "%s: %s" %(current_thread, data)
self.request.sendall(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
"""不做任何事情,从父类中继承一切所需"""
pass
if __name__ == '__main__':
# 启动服务器
server = ThreadedTCPServer((SERVER_HOST, SERVER_PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
# 使用服务器启动一个线程 -- 一个线程一个请求
server_thread = threading.Thread(target=server.serve_forever)
# 当主线程已存在的时候服务线程结束
server_thread.daemon = True
server_thread.start()
print "Server loop running on thread: %s" %server_thread.name
# 启动客户端
client(ip, port, "Hello from client 1")
client(ip, port, "Hello from client 2")
client(ip, port, "Hello from client 3")
# 服务器清理
server.shutdown()

首先创建一个服务器线程,并在后台启动。然后启动三个测试客户端,向服务器发送消息。作为响应,服务器把消息回显给客户端。在服务器请求处理类的handle()方法中,取回了当前线程的信息并将其打印出来,这些信息在每次客户端连接中都不同。在客户端和服务器的通信中用到了sendall()方法,以保证发送的数据无任何丢失。

运行结果:

wu@ubuntu:~/python$ python 2_2_threading_mixin_socket_server.py

Server loop running on thread: Thread-1

Client received: <Thread(Thread-2, started -1235223744)>: Hello from client 1

Client received: <Thread(Thread-3, started -1245709504)>: Hello from client 2

Client received: <Thread(Thread-4, started -1235223744)>: Hello from client 3


Viewing all articles
Browse latest Browse all 9596

Trending Articles