Python 创建TCP服务器的方法
问题
你想实现一个服务器,通过TCP协议和客户端通信。
解决方案
创建一个TCP服务器的一个简单方法是使用socketserver库。例如,下面是一个简单的应答服务器:
fromsocketserverimportBaseRequestHandler,TCPServer
classEchoHandler(BaseRequestHandler):
defhandle(self):
print('Gotconnectionfrom',self.client_address)
whileTrue:
msg=self.request.recv(8192)
ifnotmsg:
break
self.request.send(msg)
if__name__=='__main__':
serv=TCPServer(('',20000),EchoHandler)
serv.serve_forever()
在这段代码中,你定义了一个特殊的处理类,实现了一个handle()方法,用来为客户端连接服务。request属性是客户端socket,client_address有客户端地址。为了测试这个服务器,运行它并打开另外一个Python进程连接这个服务器:
>>>fromsocketimportsocket,AF_INET,SOCK_STREAM
>>>s=socket(AF_INET,SOCK_STREAM)
>>>s.connect(('localhost',20000))
>>>s.send(b'Hello')
5
>>>s.recv(8192)
b'Hello'
>>>
很多时候,可以很容易的定义一个不同的处理器。下面是一个使用StreamRequestHandler基类将一个类文件接口放置在底层socket上的例子:
fromsocketserverimportStreamRequestHandler,TCPServer
classEchoHandler(StreamRequestHandler):
defhandle(self):
print('Gotconnectionfrom',self.client_address)
#self.rfileisafile-likeobjectforreading
forlineinself.rfile:
#self.wfileisafile-likeobjectforwriting
self.wfile.write(line)
if__name__=='__main__':
serv=TCPServer(('',20000),EchoHandler)
serv.serve_forever()
讨论
socketserver可以让我们很容易的创建简单的TCP服务器。但是,你需要注意的是,默认情况下这种服务器是单线程的,一次只能为一个客户端连接服务。如果你想处理多个客户端,可以初始化一个ForkingTCPServer或者是ThreadingTCPServer对象。例如:
fromsocketserverimportThreadingTCPServer
if__name__=='__main__':
serv=ThreadingTCPServer(('',20000),EchoHandler)
serv.serve_forever()
使用fork或线程服务器有个潜在问题就是它们会为每个客户端连接创建一个新的进程或线程。由于客户端连接数是没有限制的,因此一个恶意的黑客可以同时发送大量的连接让你的服务器奔溃。
如果你担心这个问题,你可以创建一个预先分配大小的工作线程池或进程池。你先创建一个普通的非线程服务器,然后在一个线程池中使用serve_forever()方法来启动它们。
if__name__=='__main__':
fromthreadingimportThread
NWORKERS=16
serv=TCPServer(('',20000),EchoHandler)
forninrange(NWORKERS):
t=Thread(target=serv.serve_forever)
t.daemon=True
t.start()
serv.serve_forever()
一般来讲,一个TCPServer在实例化的时候会绑定并激活相应的socket。不过,有时候你想通过设置某些选项去调整底下的socket`,可以设置参数bind_and_activate=False。如下:
if__name__=='__main__':
serv=TCPServer(('',20000),EchoHandler,bind_and_activate=False)
#Setupvarioussocketoptions
serv.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
#Bindandactivate
serv.server_bind()
serv.server_activate()
serv.serve_forever()
上面的socket选项是一个非常普遍的配置项,它允许服务器重新绑定一个之前使用过的端口号。由于要被经常使用到,它被放置到类变量中,可以直接在TCPServer上面设置。在实例化服务器的时候去设置它的值,如下所示:
if__name__=='__main__':
TCPServer.allow_reuse_address=True
serv=TCPServer(('',20000),EchoHandler)
serv.serve_forever()
在上面示例中,我们演示了两种不同的处理器基类(BaseRequestHandler和StreamRequestHandler)。StreamRequestHandler更加灵活点,能通过设置其他的类变量来支持一些新的特性。比如:
importsocket
classEchoHandler(StreamRequestHandler):
#Optionalsettings(defaultsshown)
timeout=5#Timeoutonallsocketoperations
rbufsize=-1#Readbuffersize
wbufsize=0#Writebuffersize
disable_nagle_algorithm=False#SetsTCP_NODELAYsocketoption
defhandle(self):
print('Gotconnectionfrom',self.client_address)
try:
forlineinself.rfile:
#self.wfileisafile-likeobjectforwriting
self.wfile.write(line)
exceptsocket.timeout:
print('Timedout!')
最后,还需要注意的是绝大部分Python的高层网络模块(比如HTTP、XML-RPC等)都是建立在socketserver功能之上。也就是说,直接使用socket库来实现服务器也并不是很难。下面是一个使用socket直接编程实现的一个服务器简单例子:
fromsocketimportsocket,AF_INET,SOCK_STREAM
defecho_handler(address,client_sock):
print('Gotconnectionfrom{}'.format(address))
whileTrue:
msg=client_sock.recv(8192)
ifnotmsg:
break
client_sock.sendall(msg)
client_sock.close()
defecho_server(address,backlog=5):
sock=socket(AF_INET,SOCK_STREAM)
sock.bind(address)
sock.listen(backlog)
whileTrue:
client_sock,client_addr=sock.accept()
echo_handler(client_addr,client_sock)
if__name__=='__main__':
echo_server(('',20000))
以上就是Python创建TCP服务器的方法的详细内容,更多关于Python创建TCP服务器的资料请关注毛票票其它相关文章!