本文利用SocketServer模块完成一个简单的服务端程序,能将接收到的内容回写给客户端。然后详细分析该例子的框架。
先看例子
# -*- coding: utf-8 -*-
from time import ctime
from SocketServer import TCPServer, StreamRequestHandler
host = ''
port = 50000
addr = (host, port)
class MyRequestHandler(StreamRequestHandler):
def handle(self):
print('...connected from:', self.client_address)
self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))
line = self.rfile.readline()
while line != '\r\n':
self.wfile.write('[%s] %s' % (ctime(), line))
line = self.rfile.readline()
tcpServ = TCPServer(addr, MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()
用浏览器访问“127.0.0.1:50000”,将呈现如下结果:
这些信息是HTTP协议中请求信息,第一行表示请求方式为GET,URL相对地址是/,协议版本HTTP/1.1,以下为请求信息头。
一、服务端逻辑结构
- TCPServer接收客户端socket;
- TCPServer验证客户端socket,实质为空操作,它留给子类对之实现;
- TCPServer将客户端socket递交给MyRequestHandler;
- MyRequestHandler将接收到的内容发送给客户端,(实际上有个空行\r\n没有发送)。
二、服务端代码框架
代码流程看上面的图应该很清楚,但有几点实现细节再详述一下:
- socket属性设置,其中可以设置两个属性:
socket.SO_REUSEADDR
、socket.TCP_NODELAY
分别为设置socket端口复用(防止socket关闭后依然占用端口)不使用Nagle算法(TCP/IP中允许小包发送); - 安全关闭服务端,在其它线程调用
BaseServer.shutdown()
,该机制利用信号量threading.Event()
和布尔变量__shutdown_request
实现服务端在关闭时能保证BaseServer.serve_forever
能运行完当前循环。 - 捕获select()异常,关于
errno.EINTR
的解释:系统read()返回0,表示读取正常,如果在读的过程中遇到中断,则返回-1,并将errno置为EINTR;系统write()返回0表示无法写,如果遇到中断,则返回-1,也将errno置为EINTR;所以_eintr_retry()
在设计时遇到中断则会重次监控; - 关闭socket,socket关闭有两步:
request.shutdown(socket.SHUT_WR)
、request.close()
,第一次是关闭socket读写,第二次是使socket引用计数-1。