SocketServer是标准库中一个高级别的模块。用于简化网络客户与服务器的实现。模块中,已经实现了一些可供使用的类。
我们将再次实现之前的那个基本TCP的例子。你会注意到新实现与之前有很多相似之处,但你也要注意到,现在很多繁杂的事情已经被封装好了,你不用再去关心那个样板代码了。例子给出的是一个最简单的同步服务器。
为了要隐藏实现的细节。我们现在写程序时会使用类,这是与之前代码的另一个不同。用面向对象的方法可以帮助我们更好的组织数据与逻辑功能。你也会注意到,我们的程序现在是“事件驱动”了。这就意味着,只有在事件出现的时候,程序才有“反应”。
在之前的服务循环中,我们阻塞等待请求,有请求来的时候就处理请求,然后再回去继续等待。现在的服务循环中,就不用在服务器里写代码了,改成定义一个处理器,服务器在收到进来的请求的时候,可以调用你的处理函数。
SocketServer模块的类
Class | Description |
---|---|
BaseServer | Contains core server functionality and hooks for mix-in classes; used only for derivation so you will not create instances of this class; useTCPServer or UDPServer instead |
TCPServer/UDPServer | Basic networked synchronous TCP/UDP server |
UnixStreamServer/UnixDatagramServer | Basic file-based synchronous TCP/UDP server |
ForkingMixIn/Threading MixIn | Core forking or threading functionality; used only as mix-in classes with one of the server classes to achieve some asynchronicity; you will not instantiate this class directly |
ForkingTCPServer/ForkingUDPServer | Combination of ForkingMixIn and TCPServer/UDPServer |
ThreadingTCPServer/ThreadingUDPServer | Combination of ThreadingMixIn and TCPServer/UDPServer |
BaseRequestHandler | Contains core functionality for handling service requests; used only for derivation so you will not create instances of this class; useStreamRequestHandler or DatagramRequestHandler instead |
StreamRequest Handler/DatagramRequest-Handler | Implement service handler for TCP/UDP servers |
1. 创建一个SocketServerTCP服务器:
在代码中,先导入我们的服务器类,然后像之前一样定义主机常量。主机常量后就是我们的请求处理器类,然后是启动代码。在下面的代码片断中可以看到更多细节。
#! /usr/bin/env python
#coding=utf-8
from SocketServer import (TCPServer as TCP,StreamRequestHandler as SRH)
from time import ctime
HOST = ''
POST = 12345
ADDR = (HOST,POST)
class MyRequestHandler(SRH):
def handle(self):
print('...connected from:',self.client_address)
self.wfile.write('[%s] %s'%(ctime(),self.rfile.readline()))
tcpServ = TCP(ADDR,MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()
我们从SocketServer的StreamRequestHandler类中派生出一个子类,并重写handle()函数。在BaseRequest类中,这个函数什么也不做。在有客户消息进来的时候,handle()函数就会被调用。StreamRequestHandler 类支持像操作文件对象那样操作输入输出套接字。我们可以用readline()函数得到客户消息,用write()函数把字符串发给客户。
为了保持一致性,我们要在客户与服务器两端的代码里都加上回车与换行。实际上,你在代码中看不到这个,因为,我们重用了客户传过来的回车与换行。
2. 创建SocketServerTCP客户端
#! /usr/bin/env python
#coding=utf-8
from socket import *
HOST = 'localhost'
PORT = 12345
BUFSIZE = 1024
ADDR = (HOST,PORT)
while 1:
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
data = raw_input("> ")
if not data:
break
tcpCliSock.send("%s\r\n"%data)
data = tcpCliSock.recv(BUFSIZE)
if not data:
break
print data.strip()
tcpCliSock.close()
SocketServer的请求处理器的默认行为是接受连接,得到请求,然后就关闭连接。这使得我们不能在程序的运行时,一直保持连接状态,要每次发送数据到服务器的时候都要创建一个新的套接字。这种行为使得TCP 服务器的行为有些像UDP服务器。不过,这种行为也可以通过重写请求处理器中相应的函数来改变。
现在,我们的客户端有点完全不一样了(我们得每次都创建一个连接)。其它的小区别在服务器代码的逐行解释中已经看到了:我们使用的处理器类像文件一样操作套接字,所以我们每次都要发送行结束字符(回车与换行)。服务器只是保留并重用我们发送的行结束字符。当我们从服务器得到数据的时候,我们使用strip()函数去掉它们,然后使用print语句提供的回车。
3.
分叉SocketServer服务器:
#! /usr/bin/env python
#coding=utf-8
from SocketServer import (TCPServer as TCP,
StreamRequestHandler as SRH,
ForkingMixIn as FMI)
from time import ctime
class Server(FMI,TCP):
pass
class MyHandler(SRH):
def handle(self):
print "...connected from:",self.client_address
self.wfile.write("[%s] %s"%(ctime(),self.rfile.readline()))
HOST = ''
PORT = 12345
#BUFSIZE = 1024
ADDR = (HOST, PORT)
tcpServ = Server(ADDR,MyHandler)
tcpServ.serve_forever()
tcpServ.close()
多线程SocketServer服务器:
#! /usr/bin/env python
#coding=utf-8
from SocketServer import (TCPServer as TCP,
StreamRequestHandler as SRH,
ThreadingMixIn as TMI)
from time import ctime
class Server(TMI,TCP):
pass
class MyHandler(SRH):
def handle(self):
print("...connected from :",self.client_address)
self.wfile.write("[%s] %s"%(ctime(),self.rfile.readline()))
host = 'localhost'
port = 12345
addr = (host,port)
tcpServ = Server(addr,MyHandler)
tcpServ.serve_forever()
tcpServ.close()
REF:
1,http://blog.sina.com.cn/s/blog_6826662b0100yf4h.html
2,Core Python Programming