完整源代码 https://github.com/skyerhxx/HttpServer
简易HTTP服务器开发项目
开发环境
Python 3.7
IDE:Pycharm
功能
- 能访问 127.0.0.1:9999
- 能访问 127.0.0.1:9999/index.html
- 能够用户名和密码登录127.0.0.1:9999/index.html,然后页面会自动跳转到另一个页面
最终项目目录结构
http服务器是用到了TCP协议和HTTP协议
http server是在TCP server的基础上加了一些功能,这也正好对应了HTTP协议是TCP协议的上层
我们说的Web服务器就是HTTP服务器
传输层TCP协议
面向TCP协议的套接字服务端编程
实现TCPServer和StreamRequestHandler
实现网络服务器TCPServer类
初始化 服务器地址、处理请求类、套接字
启动服务器
接受请求
处理请求
关闭连接关闭服务器
socket_server.py
#实现TCPServer类 import socket class TCPServer: def __init__(self,server_address,handler_class): self.server_address = server_address self.HandlerClass = handler_class #处理请求的类 self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) self.is_shutdown = False #服务器的启动函数 def serve_forever(self): self.socket.bind(self.server_address) #绑定服务器地址 self.socket.listen(10) #启动并监听 #while True: while not self.is_shutdown: #1.接收请求 request, client_address = self.get_request() #2.处理请求 try: self.process_request(request, client_address) except Exception as e: print(e) finally: #3.关闭连接 self.close_request(request) #接受请求 def get_request(self): return self.socket.accept() #处理请求 def process_request(self, request, client_address): handler = self.HandlerClass(request, client_address) handler.handle() #关闭请求 def close_request(self,request): request.shutdown() request.close() #关闭服务器 def shutdown(self): self.is_shutdown = True
实现网络请求处理器Handler类
转换字节码用一个bytes()就行了
缓存是一个自己定义的list
base_handler.py
# -*- encoding=utf-8 -*- class BaseRequestHandler: def __init__(self, server, request, client_address): self.server = server self.request = request self.client_address = client_address def handle(self): #不需要做任何工作,主要由后面继承它的类去做相关工作 pass #功能:编码、解码、读写消息 class StreamRequestHandler(BaseRequestHandler): def __init__(self, server, request, client_address): BaseRequestHandler.__init__(self, server, request, client_address) self.rfile = self.request.makefile('rb') self.wfile = self.request.makefile('wb') self.wbuf = [] # 编码 # 字符串—>字节码 def encode(self, msg): if not isinstance(msg, bytes): #如果不是字节码就编码成字节码 msg = bytes(msg, encoding='utf-8') return msg # 解码 # 字节码—>字符串 def decode(self, msg): if isinstance(msg, bytes): msg = msg.decode() return msg # 读消息 def read(self, length): msg = self.rfile.read(length) return self.decode(msg) # 读取一行消息 def readline(self, length=65536): #65536是http请求报文的最大长度 msg = self.rfile.readline(length).strip() return self.decode(msg) # 写消息 #接受内容,然后写到缓存里面 def write_content(self, msg): msg = self.encode(msg) #把字符串转成字节码 self.wbuf.append(msg) # 发送消息 def send(self): for line in self.wbuf: self.wfile.write(line) self.wfile.flush() self.wbuf = [] #发送完之后清空缓冲区 def close(self): self.wfile.close() self.rfile.close()
编写网络服务器的测试用例
测试前面实现的TCPServer和Handler是否可以正常工作
编写客户端来连接服务端,测试服务端是否可以正常工作
改进成多线程
此时项目结构
应用层HTTP协议
HTTP服务器实现
实现一个支持HTTP协议的Web服务器
Handler才是真正处理HTTP请求的处理的类
实现BaseHTTPRequestHandler
继承base_handler
HTTP请求报文格式
base_http_handler.py
import logging from handler.base_handler import StreamRequestHandler logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') class BaseHTTPRequestHandler(StreamRequestHandler): def __init__(self, server, request, client_address): self.method = None self.path = None self.version = None self.headers = None self.body = None StreamRequestHandler.__init__(self,server, request, client_address) #请求的处理 def handle(self): try: #1、解析请求 if not self.parse_request(): return #2、方法执行(GET、POST) method_name = 'do_' + self.method #自检判断方法是否存在 if not hasattr(self,method_name): #发送错误 return method = getattr(self,method_name) method() #应答报文的封装 #3、发送结果 self.send() except Exception as e: logging.exception(e) #解析请求头 def parse_headers(self): #请求头是以key:value的形式存在的 headers = {} while True: line = self.readline() #如果是空行,表示请求头已经结束 if line: key,value = line.split(":",1) #这个1是分割次数,就是指分割一次 key = key.strip() value = value.strip() #将key,value保存到map中 headers[key] = value else: break return headers #解析请求 def parse_request(self): #1、解析请求行 #读取第一行————请求行 first_line = self.readline() words = first_line.split() #请求方法、请求地址、请求的HTTP版本 self.method, self.path, self.version = words #2、解析请求头 self.headers = self.parse_headers() #3、解析请求内容 #如果请求头有内容,那么它的长度将会保存在headers里面 key = 'Content-Length' if key in self.headers.keys(): #请求内容的长度 body_length = int(self.headers[key]) self.body = self.read(body_length) return True
HTTP应答报文
编写基础HTTP服务器测试工作,测试其是否正常工作
新建base_http_server.py
在原来的test.py的基础上
此时,用浏览器访问localhost:9999
添加了个do_GET方法
可以看到这些我们写的内容都能正常返回
到目前为止,我们已经把一个基础的web服务器搭建起来了,这个服务器可以
具备这三个功能,一个基础的web服务器就已经实现了,我们项目的目的也已经达到了
此时的目录结构
即HttpServer4就是一个可以实现基本功能的http服务器了
-----------------------------------------------------------------------------------------------------------------------
但是为了更好的理解GET方法以及POST方法的工作原理
我们来编写自定义HTTP应用之GET方法和POST方法
编写自定义HTTP应用之GET方法
web服务器是怎样把html页面以及图片返回给浏览器的,以及怎样进行账号和密码的校验
我们来看webserver是怎样把资源/数据返回给浏览器的
新建simple_http_handler.py
编写测试用例,看是否可用
新建simple_http_server.py
如果请求一个不存在的资源
GET方法
首先判断资源是否存在
不存在直接返回404,存在的话,首先把资源的文件打开,读取文件的长度,写到头部,再把内容读取出来,写到缓冲区。就是一行一行的读。返回浏览器之所以能成网页的界面是因为要读取的那个文件就是html的,http服务器并没有怎么处理
编写自定义HTTP应用之POST方法
POST就是提交数据到服务器端进行处理,这里以账号密码为例
来看数据提交到后台之后,后台是怎么来处理的
提交是用js来提交的
正确的用户名密码是 hxx 123456
给他设置的是登录成功后会自动跳转到我的博客
如果是错误的
至此,项目完结
此时项目结构
未来可以增添的功能
功能方面
①更完善的异常处理机制
状态码只利用了404状态码,还有很多其他的没有用
②更丰富的功能支持
日志、长连接、https安全协议
性能方面
①引入线程池
否则频繁创建和销毁线程会对性能造成一定的影响
②不使用多线程
用事件驱动引擎(epoll、select),改造成事件驱动的web服务器
参考: