http协议是基于TCP实现的, 我们所熟知的nginx, tomcat, apache等服务器,本质上就是一个tcp server, 只要你对http协议有少许的了解,就可以自己实现一个简单的web服务器,当然,它只能是一个你用来验证http协议和提升自己编程能力的小玩意。
直接上代码,然后再解释代码
import socket
# 创建socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定ip 和端口
server.bind(('0.0.0.0', 8080))
# 开始监听
server.listen(1)
html = """
<html>
<head>
<title>simple server</title>
</head>
<body>
<p>hello world</p>
</body>
</html>
"""
length = len(html.encode())
# 这里也可以不设置Content-Length, 因为Connection 是close
# 具体缘由可以参考文章《来一波原生的http请求》
head = "HTTP/1.1 200 OK\r\nServer: simple server\r\n" \
"Content-Type: text/html; charset=utf-8\r\n" \
"Content-Length: {length}\r\nConnection: close\r\n\r\n"
head = head.format(length=length)
while True:
# 等待客户端连接
clientsocket, address = server.accept()
# 接收客户端的数据
data = clientsocket.recv(1024).decode()
msg = head + html
# 向客户端发送数据
clientsocket.send(msg.encode())
# 关闭连接
clientsocket.close()
server.close()
head的定义完全遵守http协议,Content-Length 是可以不用设置的,因为Connection被设置成了close,如果是keep-alive,那么就必须设置Content-Length
消息体就是一段html,关于html可以参考文章爬虫数据分析之html
启动程序后,就可以在浏览器里输入url http://localhost:8080/ 得到的页面内容很简单,只有一个hello world
此时,只要url前面是http://localhost:8080/ ,后面不管跟什么path,得到的结果都是相同的,这是因为我们的server太简单了,没有根据path返回对应的内容,为了让你更进一步的了解http请求时都发生了什么,接下来,我要修改这个server
import socket
# 创建socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定ip 和端口
server.bind(('0.0.0.0', 8080))
# 开始监听
server.listen(1)
def get_path(data):
index = data.find("\r\n")
if index == -1:
return ""
first_line = data[:index]
arrs = first_line.split()
if len(arrs) != 3:
return ""
path = arrs[1]
return path
def get_html_by_path(path):
if path == '/':
return get_index()
elif path == "/name":
return get_name()
else:
return get_404()
html_string = """
<html>
<head>
<title>simple server</title>
</head>
<body>
<p>{content}</p>
</body>
</html>
"""
head_string = "HTTP/1.1 {status}\r\nServer: simple server\r\n" \
"Content-Type: text/html; charset=utf-8\r\n" \
"Content-Length: {length}\r\nConnection: close\r\n\r\n"
def get_html(status, content):
html = html_string.format(content=content)
length = len(html.encode())
head = head_string.format(status=status, length=length)
return head + html
def get_404():
return get_html('404 NOT FOUND', "你访问的资源不存在")
def get_name():
return get_html("200 OK", "my name is sheng")
def get_index():
return get_html("200 OK", "hello world")
while True:
# 等待客户端连接
clientsocket, address = server.accept()
# 接收客户端的数据
data = clientsocket.recv(1024).decode()
path = get_path(data)
msg = get_html_by_path(path)
# 向客户端发送数据
clientsocket.send(msg.encode())
# 关闭连接
clientsocket.close()
server.close()
升级后的server,可以对http://localhost:8080/name 和 http://localhost:8080/ 做出正确的响应,访问其他的资源则会返回404
本篇实现的服务器,是最最简单的服务器,但它和那些普遍使用的服务器,诸如nginx,tomcat在原理上是一致的,不同的是他们可以实现多进程,多线程,使用的epoll模型。
你用flask写一个web程序,最终部署的时候,用的可能是nginx,中间件用uwsgi,结构更加复杂,但本质上就是TCP server
QQ交流群: 211426309