python搭建简单的静态web服务器
[TOC]
储备知识
- 一丢丢的python(io和多线程的知识)
- 一丢丢的http协议
- 一丢丢的tcp/ip协议(当然不了解也没关系)
- 一丢丢的正则表达式知识
web服务器基本原理
当在浏览器的地址栏输入一个ip与端口之后,浏览器就会通过tcp/ip协议与相应的主机端口进程建立联系。经历过三次握手之后就会将http请求发送到相应的服务器进程去,之前我们了解的http协议在服务进程收到的其实就是一串有特殊格式的字符串。
当我们浏览器输入localhost:9876 后服务进程实际收到的如下:
大致流程
在服务端建立tcp服务进程,为了保证服务端可以同时处理多个请求,我们需要在每接受一个请求后为其单独使用一个线程(或者进程)为其进行服务。
123456789101112131415161718server = socket(AF_INET, SOCK_STREAM)server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)address = ('', 9876)server.bind(address)server.listen(10)try:while True:print("-------等待接受服务----------")client, client_address = server.accept()print("-------接受服务成功----------")# 如果使用进程服务,可以在在后面把client关闭。p = Thread(target=deal_socket, args=(client,))p.start()except Exception as e:print(e)finally:server.close()print("-------服务结束----------")这里的deal_socket函数就是我们为一个请求服务的函数,在一个单独的线程中运行。
在处理url请求的函数中,我们需要读取出客户端的http请求。
1234567891011def deal_socket(client):print("-------开启新的线程----------")try:data = client.recv(1024)if len(data) > 0:fileName = get_request_name_from_http(data.decode("utf-8"))writeHtml(client, fileName)finally:client.close()print("-------关闭新的线程----------")- 在这里的data就是我们读取到的http服务请求,当其长度等于0时代表客户端已经关闭了tcp连接。
- 这里的get_request_name_from_http()需要我们解析出请求的静态资源
- 这里的writeHtml()将静态文本写回到客户端。
在get_request_name_from_http函数中我们需要从原始的url请求中解析出http请求中我们需要的请求资源部分,这里我们可以通过正则表达式完成简单的完成解析。
1234567def get_request_name_from_http(http):# 注意这里通过非贪婪模式匹配r = re.search(r"GET /(.+?) ", http)fileName = ""if r != None:fileName = r.group(1)return fileName请求的http大概格式是这样(当我们访问http://localhost:9876/html/index.html时)
123456GET /html/index.html HTTP/1.1Host: localhost:9876Connection: keep-aliveCache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36第一行就是我们请求的静态资源,我们将其通过非贪婪模式的正则表达式扣出来。
在解析到请求的静态地址后就是简单的读取请求的文件,然后已http协议的格式返回回去就行了。
1234567891011def writeHtml(client, fileName):rspHead = NonerspBody = Noneif not os.path.exists(fileName):rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"rspBody = "file not found"else:rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"html = open(fileName, 'r', encoding='UTF-8')rspBody = html.read()client.send((rspHead + rspBody).encode("utf-8"))
下面是完整的服务代码,不到60行就可以完成一个简单的静态web服务器,这就是python的魅力:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960from socket import *from threading import Threadimport osimport redef get_request_name_from_http(http):r = re.search(r"GET /(.+?) ", http)fileName = ""if r != None:fileName = r.group(1)return fileNamedef writeHtml(client, fileName):rspHead = NonerspBody = Noneif not os.path.exists(fileName):rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"rspBody = "file not found"else:rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"html = open(fileName, 'r', encoding='UTF-8')rspBody = html.read()client.send((rspHead + rspBody).encode("utf-8"))def deal_socket(client):print("-------开启新的线程----------")try:data = client.recv(1024)if len(data) > 0:fileName = get_request_name_from_http(data.decode("utf-8"))writeHtml(client, fileName)finally:client.close()print("-------关闭新的线程----------")def main():server = socket(AF_INET, SOCK_STREAM)server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)address = ('', 9876)server.bind(address)server.listen(10)try:while True:print("-------等待接受服务----------")client, client_address = server.accept()print("-------接受服务成功----------")# 就这里和多线程不同,并且千万不能把client关掉p = Thread(target=deal_socket, args=(client,))p.start()except Exception as e:print(e)finally:server.close()print("-------服务结束----------")if __name__ == "__main__":main()到此就用python构建了史上最挫的静态wen服务器了,直接在浏览其输入静态html请求就可以显示网页了:
虽然很挫,不过web服务器的基本原理就是如此,牛逼的服务器也只是在这之上做了很多完善,下一篇我们将采用python提供的WSGI标准完成一个动态的web框架。