前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python实现简单http服务器

python实现简单http服务器

作者头像
不断折腾
发布2019-09-23 10:48:18
2.7K0
发布2019-09-23 10:48:18
举报
这实现http服务器之前,需要给大家补充一点知识,http协议。

首先http协议是基于tcp协议的,这里会用到我们前几天写的tcp服务器的知识。

我们暂且把http协议当做一个规定,就是说在浏览器访问一个页面时候,浏览器会发送一些东西给服务器,那么你发送的这些东西就是基于http协议发送的。

以百度为例我们来看一下在访问的时候,浏览器给服务器发送了什么?

1、打开谷歌浏览器

2、F12,打开开发者模式

3、输入www.baidu.com

会看到如图所示:

我们只需要先了解前两个:

GET / HTTP/1.1:GET表示请求,/表示访问主页,HTTP/1.1表示http协议1.1版本

Host:网址或者ip地址

看一下百度服务器给我们返回了什么

这些也是根据http协议返回的,那么必须有的是什么?

必须有的是第一条:HTTP/1.1 200 OK

其他都可以没有,但是我们模拟肯定要返回数据。

上面这张图叫做Headers,就是头,那我们的body部分就是我们的html页面,有了html页面,页面才变得好看。

浏览器如何区分是headers部分还是body部分?

很简单,中间加一个空行。了解了这些,我们来实现一个简单的http服务器。

其实http协议是基于tcp协议的。http协议在tcp的基础上,对服务器返回的数据的一些规定。

http服务器实例

import socket

def dump_data(cli_socket):

# 接收浏览器发送来的请求

recv_data = cli_socket.recv(1024)

# 输出浏览器给我们发送了什么

print(recv_data)

# 更具上面的http协议模拟返回数据,\r\n表示换行

resp_data = 'HTTP/1.1 200 OK\r\n'

# 一行空数据,区别headers部分和body部分

resp_data += '\r\n'

# body部分

resp_data += '<h1>hello,word.</h1>'

cli_socket.send(resp_data.encode('utf-8'))

cli_socket.close()

def main():

tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

tcp_server.bind(("",7891))

tcp_server.listen(128)

print('等待')

while True:

cli_socket,cli_addr = tcp_server.accept()

dump_data(cli_socket)

tcp_server.close()

if __name__ == "__main__":

main()

我们运行,会显示等待,等待我们的浏览器发送请求,我们打开浏览器,输入127.0.0.1:7891

就会看到我们发送给浏览器的内容,127.0.0.1表示本地,用冒号隔开,7891是我tcp服务器的端口号。这样我们就实现了一个简单的http服务器。

tcp的3次握手

第一次握手:客户端将标志位SYN赋值为1,随机产生一个参数赋值给seq,发送给服务器。等待服务器确定。

第二次握手:服务器通过SYN=1知道了该客户端要请求建立连接,再添加一个ACK=1,产生一个随机参数给seq,ack赋值为客户端随机数加1,发送给客户端确认连接,服务器进入SYN_RCVD状态。

第三次握手:客户端收到数据后,检查ack的值是否是随机数加1,ACK是否为1,正确就把ack加1,再发送给服务器,服务器确认数据,客户端和服务器都进入ESTABLISHED状态,完成三次握手。就可以传输数据了。

tcp的4次挥手

握手建立连接,挥手就是断开连接。

第一次挥手:简单来说就是发送一个数据表示我想断开连接,客户端进入FIN_WAIT_1状态。

第二次挥手:服务器收到数据,告诉客户端,我正在准备。请你确认是否断开。客户端进入FIN_WAIT_2状态

第三次挥手:发送一个数据表示我确认要断开。服务器进入LAST_ACK状态

第四次挥手:服务受收到消息,说我知道要关闭了,并且会发一个消息给服务器,之后进入TIME_WAIT。

根据浏览器的需求对应返回页面

这个程序相比上面的数据多了一个对url的判断而已。

这次我们用一个html文件来尝试。

我们在一个文件夹下创建一个txt文件,写上这样一段代码:

<html>

<head>

<meta charset="UTF-8">

<title>index</title>

</head>

<body>

<h1>我是登陆页面</h1>

</body>

</html>

将后缀名txt修改成html。

再创建一个create.html页面。只需要把里面的代码修改成:

<html>

<head>

<meta charset="UTF-8">

<title>create</title>

</head>

<body>

<h1>我是注册页面</h1>

</body>

</html>

接下来来写我们的python代码。

import socket

import re

def dump_data(cli_socket):

# 接收到的是bytes类型,我们解码

recv_data = cli_socket.recv(1024).decode('utf-8')

# 将我们接收到的数据以空格切开变成一个列表,便于我们取值,判断请求的那个页面

recv_data_lines = recv_data.splitlines()

print(recv_data_lines)

# 正则提取浏览器需要的页面

# 数据:GET /index.html HTTP/1.1

# 或者:POST /index.html HTTP/1.1 或者其他请求方式

f_name = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])

resp_data = 'HTTP/1.1 200 OK\r\n'

resp_data += '\r\n'

# body部分

# 读取文件

file = open('F:'+f_name.group(1),'rb')

html_con = file.read()

file.close()

# 由于我们读取文件是以二进制的方式读取,所以不能直接相加

cli_socket.send(resp_data.encode('utf-8'))

# 分开发送就好了

cli_socket.send(html_con)

cli_socket.close()

def main():

tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

tcp_server.bind(("",7891))

tcp_server.listen(128)

print('等待')

while True:

cli_socket,cli_addr = tcp_server.accept()

dump_data(cli_socket)

tcp_server.close()

if __name__ == "__main__":

main()

运行一下,提示我们没有favicon.ico文件(图标),我们创建一个就好了(直接文本创建该名字就好了,后缀也需要修改的)

接下来运行,我们在浏览器输入127.0.0.1:7891/index.html 页面就会显示我是登陆页面,输入127.0.0.1:7891/create.html就会显示我是注册页面,当然如果找不到文件你可以试着添加一个404页面。或者返回为HTTP/1.1 404 NOT FOUND。

完整版:

import socket

import re

def dump_data(cli_socket):

recv_data = cli_socket.recv(1024).decode('utf-8')

recv_data_lines = recv_data.splitlines()

print(recv_data_lines)

ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])

if ret:

f_name = ret.group(1)

if f_name=="/":

f_name = '/index.html'

try:

file = open('F:'+f_name,'rb')

except:

resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'

resp_data += '\r\n'

resp_data += '404'

cli_socket.send(resp_data.encode('utf-8'))

else:

html_con = file.read()

file.close()

resp_data = 'HTTP/1.1 200 OK\r\n'

resp_data += '\r\n'

cli_socket.send(resp_data.encode('utf-8'))

cli_socket.send(html_con)

cli_socket.close()

def main():

tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

tcp_server.bind(("",7891))

tcp_server.listen(128)

print('等待')

while True:

cli_socket,cli_addr = tcp_server.accept()

dump_data(cli_socket)

tcp_server.close()

if __name__ == "__main__":

main()

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python入门到放弃 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档