偶然看到有人翻译的“一起写一个 web 服务器”系列[1][2][3],感觉挺不错的,这里做一些归纳和 Python socket 编程的补充说明。
服务器层: 将来自 socket 的数据包解析为 http,调用 application,给应用提供环境信息 environ (包含 host, post, process模式, 客户端的 header, body 等)。同时给应用提供一个 start_response 的回调函数,主要在应用程序层进行响应信息处理。常见的 Web server 无法与 Web application(Flask, Django, Tornado) 直接通信,需要 WSGI server 作为桥梁。
应用程序层: 在 server 提供的 start_response 生成 header, body, status,将这些信息返回给客户端。
这部分我想根据 Web 服务器处理发给应用的请求的流程,来说说完整的 server 代码结构:def set_app()
def serve_forever
def handle_one_request()
- 对 socket 数据包进行解析 def parse_request()
- 创建 environ 字典,提供环境信息 def get_environ()
- 提供 start_response 回调函数 def start_response()
- 使用 environ 和 start_response 作为参数调用 application,并拿到返回的响应体
- 解析一次请求后,关闭 socket 端口,同时将 application 返回的数据返回至客户端 def finish_response()
代码比较简单:
def app(environ, start_response):
status = '200 OK'
response_headers = [('Content-Type', 'text/plain')]
start_response(status, response_headers)
return ['Hello world from a simple WSGI application!\n']
这里主要是对 WSGI_server 中所用的的 socket 模块进行补充
套接字格式:socket(family, type[,protocal]) 使用给定的套接族,套接字类型,协议编号(默认为0)来创建套接字Socket 模块提供了标准的 BSD Socket API
socket.AF_INET
- 用于服务器与服务器之间的网络通信
socket.SOCK_STREAM
- 基于 TCP 的流式 socket 通信
创建 TCP Socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#服务端 Socket 函数
s.listen(backlog)
- 开始监听TCP传入连接,backlog指定在拒绝链接前,操作系统可以挂起的最大连接数,该值最少为1,大部分应用程序设为5就够用了
s.bind(address)
- 将套接字绑定到地址,在AF_INET下,以tuple(host, port)的方式传入
#公共 Socket 函数
s.getsockname()
- 返回套接字自己的地址,返回值通常是一个tuple(ipaddr, port)
s.setsockopt(level,optname,value)
- 设置给定套接字选项的值。level 定义了哪个选项将被使用,通常情况下是 SOL_SOCKET。
s.getfqdn
- 返回一个name对应的完全合格的域名。如果name被忽略,将会被解释为本地主机。
可能将持续补充