Python日常笔记(28)-http协议-模仿web服务器

简单来说就是浏览器和服务器之间的一种通信协议.

看一个图来模拟客户端和服务端通信的过程

浏览器像服务器发送的请求头信息格式分析

GET /index.html HTTP/1.1
表示客户端请求的是/index.html地址,HTTP/1.1表示http协议的1.1版本,一般GET与HTTP/1.1之间显示的就是客户端访问的地址

Host: 192.168.153.1:8080
表示请求服务器的地址和端口

Connection: keep-alive
表示为长连接

Upgrade-Insecure-Requests: 1
由浏览器自己规定的

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
表示浏览器可以接收什么样的格式

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
表示客户端为什么浏览器什么版本

Accept-Encoding: gzip, deflate
表示是什么压缩格式

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
表示客户端能接收的语言

服务器返回给客户端头信息分析(以百度为例)

HTTP/1.1 200 OK
表示请求成功,客户端请求的页面有
Bdpagetype: 1
Bdqid: 0x8301fa3c00015b7e

Cache-Control: private
表示缓存是私有的

Connection: keep-alive
Content-Encoding: gzip
表示回复的压缩格式gzip

Content-Type: text/html;charset=utf-8
表示返回告诉浏览器是一个什么格式,什么编码

Date: Sat, 14 Mar 2020 03:47:21 GMT
服务器当前时间

Expires: Sat, 14 Mar 2020 03:47:19 GMT
Server: BWS/1.1
表示百度服务器的一个简称

Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=30971_1430_21124_30826_30997_30824_26350_30717; path=/; domain=.baidu.com
以上三个表示追踪用户浏览信息的

Strict-Transport-Security: max-age=172800
Traceid: 158415764105892869229440101429550340990
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

面试题:问题服务器先调用close关闭,马上重启会出现端口被占用的情况。

释放资源时一般是客户端先关闭就不会出现这种问题。
那么原因就是就是Tcp的3次握手和4次挥手,先解释一下:
3次握手就是双方准备资源
4次挥手就是将双方的资源全部释放(各自的收和发都释放)

一般来说,由于服务端需要绑定端口,先调用close之后,那么客户端就会延迟2-5分钟在进行调用close。
这时候资源并没有全部释放掉,所以马上重启服务端就会出现端口被占用的情况.

练习写一个服务器

可以直接从浏览器中输入地址即可将浏览的数据返回给客户端浏览器

# 导入socket
import socket
# 导入正则表达式
import re


def send_error(tcp_clinet_socket):
   # 否则返回一个没有找到页面
   response = "HTTP/1.1 404 NOT FOUND\r\n"
   response += "\r\n"
   response += "没有找到该页面"
   tcp_clinet_socket.send(response.encode("gbk")) # 先发送头部


# 解析数据并返回
def recv_data(tcp_clinet_socket):
   request_data = tcp_clinet_socket.recv(1024).decode("utf-8")
   # 解析数据并返回/index.html,格式GET /index.html HTTP/1.1
   str_lines = request_data.splitlines()
   if len(str_lines) == 0:
       send_error(tcp_clinet_socket) # 发送错误数据

   print(str_lines)
   # ret = re.match("[^/]+ (/[\w]+.[\w]+) ", str_lines[0])
   ret = re.match("[^/]+(/[^ ]+)", str_lines[0])
   if ret:
       file_name = ret.group(1) # index.html
       if file_name == "/":
           file_name = "/index.html"

       local_file_name = "." + file_name
       response = "HTTP/1.1 200 OK\r\n"
       response += "\r\n"
       try:
           f = open(local_file_name, "rb")
       except:
           send_error(tcp_clinet_socket) # 发送错误数据
       else:
           # 发送数据给客户端
           tcp_clinet_socket.send(response.encode("utf-8")) # 先发送头部
           # 在发送body
           return_data = f.read()
           tcp_clinet_socket.send(return_data)
   else:
       send_error(tcp_clinet_socket) # 发送错误数据

   tcp_clinet_socket.close()


def main():
   # 1.创建套接字
   tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   # 允许服务端立即使用上次绑定的端口(port)
   tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   # 2.绑定端口
   tcp_server_socket.bind(("", 9999))
   # 3.监听客户端
   tcp_server_socket.listen(128)
   while True:
       # 4.等待客户端链接
       tcp_clinet_socket, addr_socket = tcp_server_socket.accept()
       # 5.接收客户端数据
       recv_data(tcp_clinet_socket)

   tcp_server_socket.close()

if __name__ == "__main__":
   main()


随便复制的一个页面,页面的文件名最好不要使用中文,要不然会有编码问题需要解决

作者:阿超
原创公众号:『Python日常笔记』,专注于 Python爬虫等技术栈和有益的程序人生,会将一些平时的日常笔记都慢慢整理起来,也期待你的关注和阿超一起学习,公众号回复【csdn】优质资源。

发布了55 篇原创文章 · 获赞 16 · 访问量 9510

猜你喜欢

转载自blog.csdn.net/duchaochen/article/details/105069493