初学 python 写一个简单的 HTTP 服务器 实记
下面,说一说简单实现过程以及 期间发生的 现阶段自己解决不了的问题
总体来说,这个 HTTP 服务器源码没有问题的,现在分享一下
import socket
import re
def server_c(new_socket):
# 接收客户端发来的数据
request_message = new_socket.recv(1024).decode("gbk")
if request_message:
request_content1 = request_message.splitlines()
request_content2 = re.match(r"[^/]+/([^ ]*)", request_content1[0])
try:
request_content3 = request_content2.group(1)
print("------------------请求的页面为!!!", request_content3)
f = open("./" + request_content3, "rb")
content = f.read()
f.close()
response = "HTTP/1.1 200 OK\r\n"
response += "\r\n"
new_socket.send(response.encode("gbk"))
new_socket.send(content)
except Exception as aaa:
print("")
print("-----------请求的页面发生错误-------------")
print(aaa)
print("-----------------------------------------")
print("")
else:
print("")
print("------------为客户端服务完成-------------")
print("")
new_socket.close()
def main():
# 创建一个套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定 IP port
tcp_socket.bind(("192.168.8.126", 7788))
# 设置侦听套接字
tcp_socket.listen(128)
while True:
# 等待客户端的连接
new_socket, user_addr = tcp_socket.accept()
# 客户端连接完成,为客户端服务
print("----------客户端已连接--------------")
server_c(new_socket)
tcp_socket.close()
if __name__ == '__main__':
main()
下面,我简单介绍一下 这个 HTTP 服务器怎么实现的:
# 第一步,首先需要创建一个套接字,套接字是全双工的,可同时进行收发数据
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 第二步,绑定 IP 和 PORT ,因为作为服务器来说,你需要一个固定的 IP 和PORT,服务器这两个东西都是固定的,不可能说变就变,要不然 今天访问了一个网站,明天他就改变的 IP ,你有可能就访问不到了,所以这里需要绑定
tcp_socket.bind(("192.168.8.126", 7788))
# 第三步,设置侦听套接字,就像是你的电话,需要设置成响铃一样,一般来说,这里是 listen(128),最多要求可同时连接的客户端的数量为 128 个
tcp_socket.listen(128)
# 第四步,等待客户端的连接,默认会堵塞,当客户端连接的时候 才会 解堵塞
new_socket, user_addr = tcp_socket.accept()
#第五步,为客户端服务
def server_c(new_socket): //首先这是一个独立的功能,需要定义一个函数
# 接收客户端发来的数据
request_message = new_socket.recv(1024).decode("gbk")
//接收客户端发来的数据,这里接收的数据需要解码才能 显示你可以看得懂的 内容,这里我用的是 windows 做测试,所以用的 “gbk” 解码,Linux 中,一般用 “UTF-8”
if request_message: //这一步,是判断而客户端有没有 关闭,如果客户端关闭了,去执行 else 中的代码
request_content1 = request_message.splitlines()
// 这一步,因为你收到的 请求是 一行一行的,你需要提取出 第一行中有用的东西,所以先要进行分行,也就是生成一个列表,把 每一行作为 列表中的元素存放在列表中
request_content2 = re.match(r"[^/]+/([^ ]*)", request_content1[0])
//这一步,把客户端发送的请求中 第一行提取出来,然后用正则表达式 进一步提取出 第一行中有用的东西
try:
request_content3 = request_content2.group(1) //进一步,需要用正则表达式提取出 第一行中有用的东西 里面的 ——客户端需要的页面
print("------------------请求的页面为!!!", request_content3)
f = open("./" + request_content3, "rb") //去本地找到用户请求的页面,打开,读取到 content 中
content = f.read()
f.close()
response = "HTTP/1.1 200 OK\r\n"
//这里,是response 的头部信息,200 ok 表示 ”200 代表 ok“,正常的服务器,换行是需要 \r\n ,表示适合 windows 和 Linux
response += "\r\n" //这是因为,response 头部信息和 内容之间,需要一行的间距 用来区分
new_socket.send(response.encode("gbk")) //发送 response 头部信息
new_socket.send(content) //发送 response 主体内容
except Exception as aaa: // 异常检测
print("")
print("-----------请求的页面发生错误-------------")
print(aaa)
print("-----------------------------------------")
print("")
else:
print("")
print("------------为客户端服务完成-------------")
print("")
new_socket.close()
这样,基本上一个 HTTP 服务器就可以实现了
这里,实现了一个简单的 HTTP 服务器基本功能!!!
因为 现在刚上大一,自学 Python,没有任何经验,各类知识都是通过 百度搜索人家的博客来学习的。
因为之前没有接触过 python ,看到别人博客上用网页做测试,所以也就自己写了点前端页面,当我用 Dreamwaver 写的几个简单的页面来测试这个的时候,出现了这个 错误
[Errno 2] No such file or directory: './favicon.ico'
,现阶段我不知道到底应该怎么解决。找了很多博客,因为知识的欠缺,导致这个问题一直困扰着我。除了这样之外,还有一个让我看着很不顺的问题,就这 当我从浏览器访问的时候,它返回的是 没有CSS 样式的一篇极其LOW的页面。
我几乎快要崩了,我不知道是不是因为[Errno 2] No such file or directory: './favicon.ico'
这个错误引起的,但是现阶段,通过百度查阅,我还没有 办法解决。
实际上,这个网页是有CSS样式的,原本的页面应该是这样的:
我知道,这里有可能是因为我只是的缺失,我不知道什么是
favicon.ico
,以及 这个东西内部到底放着什么。。。。。。可能以后就知道了‘
’
不管怎么来说,这个HTTP服务器代码没有错误,错误出现在 我的网页源码上面!!!
这里,需要标明 HTTP 协议的三次握手,和四次挥手。
-
三次握手:
-(第一次)首先,客户端给服务器发送一个请求信息,为了能够让服务器识别这个请求信息,客户端对这个信息 用 syn
标记,即这个时候客户端 堵塞在 connect 这一步。然后(第二次),服务器收到数据,如果服务器准备好了,就给
客户端会送一个应答信息,为了让客户端可以识别这个信息,前面加上 ack 标记,这时,服务器会给这个信息附加 syn
信息,这个信息可以判断客户端是否收到 应答信息。再然后(第三次),客户端接到服务器回复的信息,connect 解堵塞,客户端
回复一个数据,告诉服务器 已经收到回复,这个数据用 ack 标记。然后,客户端个服务器就可以进行通信了。
【保证双方在收发数据之前,准备好资源】 -
四次挥手
(第一步)首先,客户端 调用 close( ),发给服务器一个数据包,这个数据包表示客户端要关闭了。然后(第二步),服务器收到通知后,返回一个 确认收到客户端关闭通知的数据包,然后这个时候,客户端关闭发送通道 。再然后(第三步),服务器 发送一个让客户端 接受数据的通道关闭的数据包。再然后(第四步),客户端 收到通知,回复一个收到通知的数据包给服务器。。。。。。此时,客户端会等待 2MSL (数据包在网络上传输最大延迟时间),大约 2分多钟,如果由于什么原因,数据包没有发送出去,服务器没收到,此时服务器在 1MSL 的时候会重复发送 第二步 的通知数据包。这个时候客户端收到,再次发送收到通知的数据包。。。。。。这样为了保证第三步,当客户端因为网络原因发不出数据包的时候,服务器收不到消息继续发送,资源保留2分钟左右不释放。这也就是为什么。。。。客户端先关闭 通信的原因。 因为先关闭的 一方,资源会保留2分钟左右,这个时候 端口不能重复使用。
【这里第二步和第三步没有向三次握手一样合并的原因,是因为: 第二步,服务器收到 客户端发送通道关闭消息,发送数据包为了给客户端回信,已收到消息。而第三次,是因为 客户端 发送通道关闭以后,服务器的 recv 解堵塞,往下执行 如果有new_socket.close( ),则发送关闭服务器与客户端 通信的消息,通知客户端关闭 接受消息的通道。第三步和第二步没有关联的,假如合并了,如果服务器里没有 new_socket.close( ),也就是说连第二步也发不出去了。】