HTTP报文与TCP连接数据传送研究

chaoLucky 2024年07月26日 522次浏览

前言

都知道超文本传输协议(http)是承载于tcp连接之上的,并且一个http请求至少要发起一次tcp链接,那么,他们之间的数据交换究竟是怎么样的呢?

HTTP报文

常见的就是浏览器作为一个TCP客户端,HTTP服务器其实就是一个TCP服务端,比如nginx,在运行之后,nginx会监听指定的端口,一般是80端口,https则默认是443;
TCP上是可以发送任意数据的,最底层就是二进制,就像串口一样,之后根据编码解析成数据,比如UTF-8,再深入可以去了解网络的7层模型;
发起一个请求,其实就是传输了一段文本至HTTP(TCP)的服务端,而请求什么,请求条件,请求之后要怎么样,都是在HTTP报文头部来确定,下面就是一个HTTP报文,它发起了一个GET请求,请求网站的根目录:/

GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Accept-Encoding: deflate
Cache-Control: no-cache
DNT: 1
Host: www.baidu.com
Pragma: no-cache
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36

(注意:1)

注意点1:此处结尾一定要有回车换行符,否则服务器会一直处于接收状态,视为未发送完整内容

可见HTTP报文头部内容是类似key:value的键值对,key为设置项,或者也叫配置项,后面的内容为值,http server在得到这一串数据之后,就会进行解析,比如开头的GET,表示这是一个GET请求,使用http1.1版本,Accept告诉了服务器允许接收的数据类型,下面约定了语言,是否压缩,缓存与否,用户标识,是否保持链接等等等等,服务器会根据请求的内容进行响应。

下面是HTTP报文格式

baowengeshi

下面是常见的HTTP报文含义

请求头 含义
User-Agent 产生请求的浏览器类型,User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器
Accept 客户端可识别的响应内容类型列表。eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。
Accept-Language 客户端可接受的自然语言
Accept-chartset 客户端可接受应答的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。
Accept-Encoding 客户端可接受的编码压缩格式
HOST 请求的主机名称,允许多个域名同处一个IP之地,即虚拟主机
Connection 连接方式(close或keep-alive)
Cookie 存储于客户端扩展字段,向同一域名的服务端发送该域的cookie
Authorization Authorization请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。

实践

使用上面的内容HTTP报文请求百度首页:

屏幕截图 2024-07-26 163853

根据上图可发现,百度服务器响应了一段HTTP报文,以及HTML内容,其中HTTP状态码为302,说明需要切换地址,因为这里请求的是80段口的http,而百度默认使用的是https进行对外服务,https与http相比,前者更加安全,传输过程中为加密传输,而http为明文,所以我们能看到具体内容。

GZIP
上面请求的是普通页面,那是否可以请求文件呢?比如css文件?当然是可以的。我们来试试,这次使用bpic.chaolucky.com下的css文件:

GET /static/css/vue.css HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
DNT: 1
Host: bpic.chaolucky.com
Pragma: no-cache
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36

这个请求头请求了一个/static/css/vue.css文件,我们来看响应:

屏幕截图 2024-07-26 164345

服务器的响应头没有问题,但是,,嗯。。?好像内容是乱码?这是怎么回事?
其实这是因为请求头中Accept-Encoding: gzip, deflate的gzip导致的。由于传输时一些文件比较大,会影响传输的时间,所以一般会默认开启gzip压缩,来保证传输的内容相对减少一点,我们给gzip去掉试试:

屏幕截图 2024-07-26 164714

好了,这次就是返回了我们平时见到的一模一样的css内容了,js文件同理。

服务端?

如果我们来当服务端会怎么样?是不是就可以对外提供HTTP服务了?答案是当然可以。将软件修改成TCP Server模式,地址为本机地址,端口我们使用8080:

屏幕截图 2024-07-26 164932

之后打开浏览器,输入网址 http://127.0.0.1:8080/之后回车,会发现浏览器处于加载卡死状态,然后我们的服务端收到了来自浏览器的请求报文:

屏幕截图 2024-07-26 165115

我们复制一份响应头,然后发送出去:

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 26 Jul 2024 08:02:09 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 710
Connection: close
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: Content-Type
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sun, 13 Nov 2022 07:55:22 GMT
ETag: W/"2c6-1846ffc6210"
X-Cache: MISS


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TCP测试</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="description" content="Description">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<body>
    <h1>这是一个TCP服务端手动发送来的请求</h1>
    <div id="app">这是一个TCP服务端手动发动来的请求</div>
</body>
</html>

屏幕截图 2024-07-26 165155

发现浏览器获取到了我们发送的内容,并且正确解析了其中的html代码

如果这时打开浏览器的F12,找到对应请求的timing,会发现大部分时间都是在Waiting For Server Resopnse.所以以后在前端加载优化的时候,可以根据这个来判断是前端加载还是后端加载问题,具体是哪一环出了问题。


请求头中有一个Connection,可以选择为Keep-Alive,在一次性有很多请求的时候,一定要选择这个,这样可以复用TCP链接,否则每一次请求都需要建立一次TCP链接,每次都需要三次握手四次挥手,虽然手不累,但是时间会变久,这也是可优化项之一。


就酱!可以自己动手研究一下,这里没有提到POST以及HTTPS。