SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
创建服务器的步骤。首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。
集成ThreadingMixIn类时需要处理异常关闭。daemon_threads指示服务器是否要等待线程终止,要是线程互相独立,必须要设置为True,默认是False。
无论用什么网络协议,服务器类有相同的外部方法和属性。
该模块在python3中已经更名为socketserver。
服务器类型
5种类型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。 注意:BaseServer不直接对外服务。
服务器对象
class SocketServer.BaseServer:这是模块中的所有服务器对象的超类。它定义了接口,如下所述,但是大多数的方法不实现,在子类中进行细化。
BaseServer.fileno():返回服务器监听套接字的整数文件描述符。通常用来传递给select.select(), 以允许一个进程监视多个服务器。
BaseServer.handle_request():处理单个请求。处理顺序:get_request(), verify_request(), process_request()。如果用户提供handle()方法抛出异常,将调用服务器的handle_error()方法。如果self.timeout内没有请求收到, 将调用handle_timeout()并返回handle_request()。
BaseServer.serve_forever(poll_interval=0.5): 处理请求,直到一个明确的shutdown()请求。每poll_interval秒轮询一次shutdown。忽略self.timeout。如果你需要做周期性的任务,建议放置在其他线程。
BaseServer.shutdown():告诉serve_forever()循环停止并等待其停止。python2.6版本。
BaseServer.address_family: 地址家族,比如socket.AF_INET和socket.AF_UNIX。
BaseServer.RequestHandlerClass:用户提供的请求处理类,这个类为每个请求创建实例。
BaseServer.server_address:服务器侦听的地址。格式根据协议家族地址的各不相同,请参阅socket模块的文档。
BaseServer.socketSocket:服务器上侦听传入的请求socket对象的服务器。
服务器类支持下面的类变量:
BaseServer.allow_reuse_address:服务器是否允许地址的重用。默认为false ,并且可在子类中更改。
BaseServer.request_queue_size
请求队列的大小。如果单个请求需要很长的时间来处理,服务器忙时请求被放置到队列中,最多可以放request_queue_size个。一旦队列已满,来自客户端的请求将得到 “Connection denied”错误。默认值通常为5 ,但可以被子类覆盖。
BaseServer.socket_type:服务器使用的套接字类型; socket.SOCK_STREAM和socket.SOCK_DGRAM等。
BaseServer.timeout:超时时间,以秒为单位,或 None表示没有超时。如果handle_request()在timeout内没有收到请求,将调用handle_timeout()。
下面方法可以被子类重载,它们对服务器对象的外部用户没有影响。
BaseServer.finish_request():实际处理RequestHandlerClass发起的请求并调用其handle()方法。 常用。
BaseServer.get_request():接受socket请求,并返回二元组包含要用于与客户端通信的新socket对象,以及客户端的地址。
BaseServer.handle_error(request, client_address):如果RequestHandlerClass的handle()方法抛出异常时调用。默认操作是打印traceback到标准输出,并继续处理其他请求。
BaseServer.handle_timeout():超时处理。默认对于forking服务器是收集退出的子进程状态,threading服务器则什么都不做。
BaseServer.process_request(request, client_address) :调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点。常用。
BaseServer.server_activate():通过服务器的构造函数来激活服务器。默认的行为只是监听服务器套接字。可重载。
BaseServer.server_bind():通过服务器的构造函数中调用绑定socket到所需的地址。可重载。
BaseServer.verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求。 常用。
请求处理器
处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:
setup(): 准备请求处理. 默认什么都不做,StreamRequestHandler中会创建文件类似的对象以读写socket.
handle(): 处理请求。解析传入的请求,处理数据,并发送响应。默认什么都不做。常用变量:self.request,self.client_address,self.server。
finish(): 环境清理。默认什么都不做,如果setup产生异常,不会执行finish。
通常只需要重载handle。self.request的类型和数据报或流的服务不同。对于流服务,self.request是socket 对象;对于数据报服务,self.request是字符串和socket 。可以在子类StreamRequestHandler或DatagramRequestHandler中重载,重写setup()和finish() ,并提供self.rfile和self.wfile属性。 self.rfile和self.wfile可以读取或写入,以获得请求数据或将数据返回到客户端。
Echo实例
TCPServer
TCPServer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import
SocketServerclass MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def
handle(
self
):
# self.request is the TCP socket connected to the client
self
.data
=
self
.request.recv(
1024
).strip()
print
"{} wrote:"
.
format
(
self
.client_address[
0
])
print
self
.data
# just send back the same data, but upper-cased
self
.request.sendall(
self
.data.upper())
if
__name__
=
=
"__main__"
:
HOST, PORT
=
"localhost"
,
9999
# Create the server, binding to localhost on port 9999
server
=
SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
|
另外一种方式是使用流,一次读一行。
1
2
3
4
5
6
7
8
9
10
|
class
MyTCPHandler(SocketServer.StreamRequestHandler):
def
handle(
self
):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
self
.data
=
self
.rfile.readline().strip()
print
"{} wrote:"
.
format
(
self
.client_address[
0
])
print
self
.data
# Likewise, self.wfile is a file-like object used to write back
# to the client
self
.wfile.write(
self
.data.upper())
|
客户端:
1
2
3
4
5
6
7
8
|
import
socketimport sysHOST, PORT
=
"localhost"
,
9999data
=
" "
.join(sys.argv[
1
:])
# Create a socket (SOCK_STREAM means a TCP socket)sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:
# Connect to server and send data
sock.connect((HOST, PORT))
sock.sendall(data
+
"\n"
)
# Receive data from the server and shut down
received
=
sock.recv(
1024
)
finally
:
sock.close()
print
"Sent: {}"
.
format
(data)
print
"Received: {}"
.
format
(received)
|
《The Python Standard Library by Example 2011》有更详细的echo实例,参见11.3.5部分。 执行结果:
1
2
3
4
5
6
7
8
9
10
|
# python TCPServer.py
127.0.0.1 wrote:
hello world with TCP
127.0.0.1 wrote:
python is
nice
# python TCPClient.py
Sent:
Received:
# python TCPClient.py hello world with TCPSent: hello world with TCP
Received: HELLO WORLD WITH TCP
# python TCPClient.py python is niceSent: python is nice
Received: PYTHON IS NICE
|
UDPServer
UDPServer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import
SocketServerclass MyUDPHandler(SocketServer.BaseRequestHandler):
"""
This class works similar to the TCP handler class, except that
self.request consists of a pair of data and client socket, and since
there is no connection the client address must be given explicitly
when sending data back via sendto().
"""
def
handle(
self
):
data
=
self
.request[
0
].strip()
socket
=
self
.request[
1
]
print
"{} wrote:"
.
format
(
self
.client_address[
0
])
print
data
socket.sendto(data.upper(),
self
.client_address)
if
__name__
=
=
"__main__"
:
HOST, PORT
=
"localhost"
,
9999
server
=
SocketServer.UDPServer((HOST, PORT), MyUDPHandler)
server.serve_forever()
|
UDPClient.py
1
|
import
socketimport sysHOST, PORT
=
"localhost"
,
9999data
=
" "
.join(sys.argv[
1
:])
# SOCK_DGRAM is the socket type to use for UDP socketssock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# As you can see, there is no connect() call; UDP has no connections.# Instead, data is directly sent to the recipient via sendto().sock.sendto(data + "\n", (HOST, PORT))received = sock.recv(1024)print "Sent: {}".format(data)print "Received: {}".format(received)
|
执行和UDP类似。
异步
ThreadingMixIn的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import
socketimport threadingimport SocketServerclass ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def
handle(
self
):
data
=
self
.request.recv(
1024
)
cur_thread
=
threading.current_thread()
response
=
"{}: {}"
.
format
(cur_thread.name, data)
self
.request.sendall(response)
class
ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
passdef client(ip, port, message):
sock
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
try
:
sock.sendall(message)
response
=
sock.recv(
1024
)
print
"Received: {}"
.
format
(response)
finally
:
sock.close()
if
__name__
=
=
"__main__"
:
# Port 0 means to select an arbitrary unused port
HOST, PORT
=
"localhost"
,
0
server
=
ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port
=
server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread
=
threading.Thread(target
=
server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon
=
True
server_thread.start()
print
"Server loop running in thread:"
, server_thread.name
client(ip, port,
"Hello World 1"
)
client(ip, port,
"Hello World 2"
)
client(ip, port,
"Hello World 3"
)
server.shutdown()
|
执行结果:
1
2
3
4
5
|
$ python ThreadedTCPServer.py
Server loop running
in
thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3
|
ForkingMixIn的使用方法类似,只不过是用进程代替了线程。《The Python Standard Library by Example 2011》中有相关实例。
本文地址
http://automationtesting.sinaapp.com/blog/m_socket_connection
本站地址:python自动化测试http://automationtesting.sinaapp.com python开发自动化测试群113938272和开发测试群6089740 微博 http://weibo.com/cizhenshi
参考资料
《The Python Standard Library by Example 2011》
socket (http://docs.python.org/library/socket.html) The standard library documentation for this module.