计算机网络:TCP与HTTP协议

Posted by Mars . Modified at

计算机网络笔记: TCP和HTTP部分

一、TCP协议及其特点

TCP协议是TCP/IP协议栈里面,用于运输层的协议。

TCP协议的特点:

  1. 面向连接:必须先握手建立连接再通信,通信完协商断开连接;

面向连接和有连接的关系?

面向连接的连接状态信息,只在连接的端点保存。而有连接的情况,是除了端点之外,中点保障二者通信的网络节点也保存它们的连接信息。

  1. 可靠交付:保证接收方能可靠接收到发送的全部数据,且双方数据完全一致;
  2. 流量控制:发送方发送的数据流量,不会淹没接收方;
  3. 点对点:TCP连接是一对一的(两端均由套接字Socket决定: IP+端口号);
  4. 全双工:双方可以随时互相发送数据;
  5. TCP头部占用20字节

同一个IP可以有多个TCP连接,同一个端口号也可以出现在多个TCP连接中。

浏览器:一个域名最多只能同时创建6-8个TCP连接。

这些特点与UDP相互形成对比,UDP的特点:

  • 无连接:不需要提前建立连接;
  • 面向报文:一次发送一整个报文。不进行任何分割,加上UDP首部后,直接传递给网络层;
  • 既不保证可靠交付,也不保证按序到达
  • 不进行流量控制:即收即发,即使拥塞也照常发送数据;
  • 支持广播:可以一对一、一对多、多对多;
  • 头部字节较少:UDP的头部只有8字节;

TCP和UDP的典型应用

TCP: Web、FTP、Telnet远程登录、SMTP(EMail)等;

UDP: 流媒体、DNS、远程会议、网络电话(基本都是实时性要求高的应用);

1.1 TCP报文

TCP协议的首部由20字节的固定部分和4n字节的可变部分构成。其固定部分组成结构如下:

TCP

源端口、目标端口(各16位)

注明数据来源和目标的端口号。

序号(32位)

在TCP传输中,每一个字节都按顺序被标上序号。整个字节流的传输起始序号在TCP连接建立握手时指定

TCP报文首部的序号,表示这个报文段第一个字节的序号。

为什么不一直用固定的起始字节序号,比如0,开始传输?

因为TCP连接的两个端口,未必只进行一次连接,且TCP报文在传输的时候可能会在网络中滞留。如果都从一个字节序号开始,假设我们先后在两个相同设备的相同端口,建立了两个TCP连接(建立 -> 关闭 -> 建立),那么前一个建立的连接中滞留的报文,有可能在第二次建立的连接中到达,而且因为序号没有区别,会被正常接收,这样就造成了错误。

TCP连接的起始序号是如何选择的?

可以取时钟信号的低32位。这样也虽然存在冲突的可能,但是概率极低。

确认号(32位)

是对方期望接收的下一个报文段第一个字节的序号。

确认号是接收方返回报文的重要内容。如果返回的确认号是N,则代表N-1之前的(包括N-1)序号的字节都已经被正确接收。

数据偏移(4位)

指出TCP报文中,数据起始位置距离报文起点有多远。(实际上就是指出了报文首部的长度。) 单位是4字节(32位)。也就是说,数据偏移如果是5,则代表数据第一个字节在距离起始点20字节位置处。 4位二进制数最大能表示的数字是15,因此 TCP报文首部最大长度为60字节,也就是说选项部分长度不能超过40字节。

保留(6位)

暂时无用。应该全部置0.

紧急标志URG(1位)

老字段,一般不用。

URG置1,则代表该报文段数据是紧急数据。 紧急数据会被TCP插入本报文段数据的最前面,而不是在后面排队。

确认标志ACK(1位)

确认标志ACK=1时,确认号字段才是有效的。 TCP规定,在连接建立之后,所有报文段都必须将ACK置1.

推送标志PSH(1位)

如果希望对方在收到报文后立即响应,可以将PSH置1. PSH=1时,表示推送信息,对方在收到后将不再等待TCP缓存填满才上传给应用层,而是直接上传立即更新信息。

复位RST(1位)

RST=1时,表示TCP连接出现严重差错,必须释放然后重新建立连接。 RST=1还用来拒绝一个非法的报文段或拒绝打开一个连接。

同步标志SYN(1位)

用于在TCP连接建立时同步序号。SYN为1代表请求建立连接。 当SYN=1、ACK=0,表示这是一个请求连接的报文段。当SYN=1、ACK=1时表示这是一个同意建立连接的响应报文。

终止标志FIN(1位)

用来释放一个连接。FIN=1表示数据已经发送完毕,请求释放一个连接。

窗口(16位)

指的是发送本报文一端的接收窗口大小。 单位是1字节。

如果接收的窗口字段为80,则代表如下含义: 对方缓存中还有80字节的空位,可以向它再传80字节的数据。

窗口值是发送方设置发送窗口大小的重要依据。(不能大于接收方的接收窗口大小)

检验和(16位)

检验和检验的范围包含首部和数据两部分。 检验和的计算与UDP一样,需要加一个伪首部。

紧急指针(16位)

紧急指针只有在URG=1时才有意义。它指出了紧急数据末尾在报文段中的位置。 通过它可计算本报文数据中的紧急数据的长度。紧急数据之后就还是普通数据。

选项(位数可变)

最长为40字节。如果使用后没凑满4字节的整数倍,则必须用填充0凑够4字节。 最初只有一种功能: 配置最大报文段长度MSS。(MSS代表每个TCP报文段中的数据部分的最大长度。) 默认情况下,如果没有配置MSS值,则MSS=536字节。 因此,所有互联网上的主机都必须能接受536+20=556字节的TCP报文段。 后来又加入了窗口扩大、时间戳和选择确认选项。

1.2 TCP协议如何实现可靠传输

发送窗口

发送窗口是发送数据流中的一段,有一个前沿和一个后沿。前沿是能发送的最远的数据位置,后沿是发送且已确认的最大序号字节位置。前后沿之间的字节位置就是发送窗口。

发送窗口表示:在没有收到接收端确认的情况下,发送方可以将发送窗口中的全部数据都发送给对方。凡是没有收到对方确认的数据都必须暂时保留,以便在超时传送中重用。

随着接收端确认信息的收到和窗口字段数的改变,发送窗口动态改变。已确认发送成功的字节发送端可丢弃,同时发送窗口后沿向前移动。

发送窗口的后沿只能向前移动或不动,不能向后移动。(已确认发送成功的信息不能撤回)

发送窗口的前沿根据接收窗口的大小和确认号可以向前或不动,也可以向后移动(但是TCP协议一般非常不建议这样做。)

发送窗口并不时刻保持与接收窗口一样大。因为网络时延和拥塞情况,发送窗口一般小于接收窗口。

接收窗口

接收窗口位于接收端,也有前沿和后沿。

接收窗口后沿取决于已返回确认的最大字节序号。前沿取决于接收端TCP缓存的大小。

1.3 TCP连接的建立与释放

建立链接:三次握手

TCP建立链接三次握手示意图

TCP连接建立的过程叫做握手。需要在客户和服务器之间进行三次握手才能建立TCP连接。

为什么两次握手是不行的?

  1. 半连接:发送方向接收方请求建立连接,接收方回复的确认连接信息可能丢失,造成接收方建立了虚假的半连接,并为虚假连接分配了资源,实际上发送方并未建立起真正的连接;
  2. 旧数据被新连接接收:因为具有超时重传机制,发送方请求建立连接的信息,如果没收到确认也会超时重传。如果两次握手,可能会造成发送方发送的旧数据,被超时重传后建立的新连接,当做新数据接收;

① 请求连接握手

请求方发出连接请求报文段。

置SYN=1,ACK=0,同时给出序号seq=x;

② 同意建立连接确认

连接被请求方对TCP连接请求给出确认。

置SYN=1,ACK=1,同时给出序号seq=y和确认号ack=x+1;

③ 请求方再次给出确认

请求方对被请求方给出的确认,再次进行确认。(防止“已失效的连接请求报文段”问题,见P239)

置SYN=0、ACK=1,同时seq=x+1,ack=y+1.

TCP连接释放:四报文挥手

TCP连接释放过程需要进行四次握手。

TCP链接释放四次挥手示意图

① 连接释放请求端发送请求释放连接握手

请求端请求释放连接。发出握手信号。

置FIN=1、ACK=0、seq=u(当前序号)

② 对①信号的确认

被请求端进行确认。

置ACK=1、seq=v(当前序号)、ack=u+1(当前确认号)

③ 等待被请求方数据传输完毕

②过后,被请求方依然可以向对方发送数据。直到数据传送完毕。

④ 被请求方再次发送释放连接确认

数据传输完毕后,被请求方向对方再次发送可关闭连接的确认信号。

置FIN=1、ACK=1、seq=w(当前序号)、ack=u+1(★!!这里必须再次重复②中第一次确认的确认号!)

⑤ 请求方最后一次给出确认

请求方收到对方第二次确认后,发送最后一次确认,进入时间等待状态(TIME-WAIT, 等待时长为2MSL,一个MSL TCP建议为两分钟,实际情况可适当缩短)。

置ACK=1、seq=u+1(当前序号)、ack=w+1(当前确认号)

⑥ 被请求方收到后不再给出确认,直接释放连接。

⑦ 2MSL时间到后,请求方也释放连接。

为什么要设置2MSL等待时间这种挥手机制?

因为TCP连接释放过程,无法进行完美确认。因为最后一次发送的确认断开报文,对方是否能够收到是不可知的。(如果想知道,必须要再发送一个确认报文,让对方应答,这样对方反过来也面临一个同样的问题)

1.4 TCP的慢开始

TCP的慢开始是一种TCP拥塞控制方法。

在TCP发送方刚开始发送数据的时候,由于不知道网络的负载情况,先从一个小的发送窗口开始,每次收到确认,对发送窗口进行扩大。

这样有效防止了网络拥塞,但是相对于直接设置大窗口一次性发送大量数据,TCP的传输速率变低。

1.5 浏览器中的TCP连接

浏览器中,一个域名最多只能同时创建6个TCP连接。

浏览器TCP连接

因此,多个请求同时处理,会发生队头阻塞(HTTP队头阻塞)。

二、HTTP超文本传输协议

HTTP协议定义了浏览器(万维网客户)怎样向万维网请求文档,也定义了服务器怎样把文档传送给浏览器。

HTTP用TCP协议作为运输层协议。

HTTP协议本身是无连接的(不需要提前建立连接),但TCP协议是面向连接的

HTTP协议是无状态的(Stateless)。也就是说,每次向服务器发出HTTP请求,服务器都同等对待,不会因为之前的请求而改变响应内容。(但是后来因为业务需要,使用Cookies这类技术可以实现对双方的识别。)

2.1 HTTP请求与响应的过程

主机发出HTTP请求时,HTTP协议首先要和服务器建立TCP连接。

建立TCP连接需要三报文握手。前两个报文需要一个RTT往返时间,在第三个报文(主机二次确认报文)时,HTTP就把请求报文放入TCP的数据部分,发送给服务器。

服务器收到后,返回请求的文档给主机(先传输)。因此发送一个HTTP请求并相应所需的时间为:

HTTP文档传输的时间 + 2×RTT(往返时间)

2.2 HTTP的版本、各版本改进点

目前HTTP版本主要有,HTTP/1.0、HTTP/1.1HTTP/2

2.2.1 HTTP/1.0

发布于1996年。

  • 默认不支持TCP长链接:每次HTTP通信完成,断开TCP连接;
  • 缓存使用If-Modified-SinceExpires判断;

2.2.2 HTTP/1.1 改进点

HTTP/1.1相较于HTTP/1.0改进了以下几点:

  1. 长连接;
    • 不每次断开TCP连接,只要C/S任意一方没有明确提出终止连接,则保持TCP连接;
    • HTTP/1.1中,所有连接默认为持久连接;
    • 长连接首部行:Connection: keep-alive / close
  2. 管线化(Pipelining);
    • 可以一次性并行发出多个请求,服务器按请求顺序进行响应。而不是每次发出一个请求,然后等待响应接收完成再发出下一个请求;
    • 缺点:
      • 仍存在队头阻塞问题。如果先发出的请求在服务器端响应慢,仍然阻塞后续响应;
      • 服务器压力大,为了按序返回,需要缓存多个响应;
      • 浏览器中途断连服务器,需要重新处理多个请求;
  3. 添加ETagIf-MatchIf-None-Match等缓存相关首部行;
  4. 添加Host首部行,记录主机名(因为虚拟主机后,一个IP地址可以对应多台虚拟主机),且强制请求必须带有Host头部。
  5. 新增24个错误状态码;

2.2.3 HTTP/2.0 改进点

  1. 新的二进制格式
    • 以二进制帧为最小单位传输,原本的报文消息被划分为更小的数据帧;
    • 解析不再基于文本,而是变成基于二进制,避免了文本解析的复杂性;
  2. 多路复用
    • 一个Http请求,被当做一个流Stream,每个流有自己对应的Stream ID;
    • 每一个二进制数据帧,都包含有它从属于哪个流的Stream ID;
    • 在一个TCP连接上,可以交替发送从属于任意流的数据帧,在接收时,拼接每个流的数据帧成为完整数据。
    • 优点:
      • 解决了应用层队头堵塞问题,响应慢不影响下一次请求的发送;
      • 只需要一个TCP连接。减少了TCP连接数,避开了TCP慢启动问题;
  3. Header压缩
    • 使用HPACK算法,避免了重复传输header,压缩了首部的体积;
  4. 服务端推送
    • 客户端请求一个资源,与这个资源相关的后续资源服务器一并推送给客户端,由客户端缓存,减少了请求次数;
  5. 应用层重置连接
    • 直接通过特殊类型的帧,从应用层关闭某一流,无需关闭TCP连接;
  6. 请求优先级设置
    • 每个流都可以设置权重优先级,关键请求优先响应;
  7. 流量控制
    • HTTP2.0中,每一方都向对方公开自己的流量窗口,限制另一方发送数据的大小;

HTTP2.0的缺点

所有数据帧共用1个TCP连接,因为TCP是按序确认,可靠交付,一旦中途发生丢包,整体传输效率变差。

当丢包率大于2%时,效率不如Http1.1;

2.2.4 HTTP/3.0:QUIC协议(Quick UDP Internet Connections,快速UDP互联网连接)

  • 基于UDP,实现TCP的流量控制、可靠传输功能;
  • 集成了TLS加密功能;
  • 多路复用,实现了多路数据流单独传输,避免队头阻塞;
  • 快速握手,0RTT或1RTT时间建立连接;

QUIC无法推广的原因:

  1. 不再基于TCP而是UDP,普遍系统对UDP的优化程度较低;
  2. 网络中间设备对UDP的优化程度远低于TCP,容易丢包;
  3. 服务器和浏览器支持程度差。

2.3 ★ HTTP报文结构

HTTP是面向文本的。所以,报文的每个字段都是ASCII码串,各个字段的长度都是不确定的。

HTTP报文结构图

2.3.1 请求报文

2.3.1.1 请求报文结构

请求报文分为:开始行、首部行 和 实体主体。

开始行:也叫做请求行。开始行的三个字段(方法、URI、版本)之间都以空格分隔开。最后是CR(回车)和LF(换行)。

首部行:用来说明浏览器、服务器或报文主体的一些信息。首部可以有很多行,也可以不使用。每一个首部行都有首部字段名和它的值,结尾为CRLF(回车换行)。

首部行的顺序没有硬性规定。但是一般建议控制数据作为第一行。(请求是host,响应是date)

实体主体:请求部分一般不使用这个字段,用于响应报文回复内容使用。

2.3.1.2 请求方法 Method

方法(操作) 意义
OPTION 请求一些选项的信息。
GET 请求读取由URL所标志的信息。
HEAD 请求读取由URL所标志的信息的首部。
POST 给服务器添加信息。
PUT 在指明的URL下储存一个文档
DELETE 删除URL所标志的资源
TRACE(已废弃) 用来进行环回测试。(已废弃。)
CONNECT 用于代理服务器。

2.3.1.3 get/post请求方法的区别

  • 语义上有区别,GET是请求数据,POST是上传数据;
  • GET请求通过URI携带信息,浏览器和服务器对其长度有限制。POST请求通过请求主体request body携带信息,参数大小无限制;
  • GET请求会保存在浏览器历史记录中,以供缓存,而POST不会;
  • GET操作是幂等的,多次操作最终效果相同。而POST操作不是幂等的;

2.3.2 响应报文

响应状态码:

HTTP响应状态码

2.3.3 容易混淆的首部行:host/origin/referer

  • host:(格式:域名+端口号)

Http1.1规定请求时必须携带(没有或超过1个都会返回400BadRequest)。告诉服务器请求的资源所处的域名和端口号;

因为虚拟主机技术的出现,可以把一台物理服务器,分为多个互联网主机,运行多个网站服务。

所以,http1.1规定必须指明host首部行,让ip对应的物理服务器可以识别请求的资源位于它下方哪个域名+端口中。

  • origin: (格式:协议+域名+端口号)

origin表示跨域请求或预检请求的来源站点(也就是同源策略等所提到的)。

origin只包含请求来源页面的协议、域名、端口号,不包含任何路径信息

只有以下两种情况,会携带origin首部:

  1. 跨域请求;
  2. 同域的POST请求。
  • referer:(默认格式:协议+域名+端口号+路径+参数)

当前请求的来源页面的地址。referer不包含url中的hash值!

referer表示当前请求是通过哪个页面发起的。比如通过www.baidu.com上的链接点击进入另一个页面,或者发起的ajax请求,都会携带referer: https://www.baidu.com/ 这个首部行。

以下几种情况,referer不会被发送:

  • 来源页面采用的协议,为表示本地文件的 “file” 或者 “data” URI;
  • 当前请求页面采用的是非安全协议(HTTP),而来源页面采用的是安全协议(HTTPS);
  • 直接输入网址或通过浏览器书签访问;
  • 使用 html5 中 rel=noreferrer 属性。<a href="/test/index.php?abc" rel="noreferrer" target="_blank">noreferrer</a>

Referrer Policy 的设置方法

是否随请求发送referer,通过一种叫做referrer policy的策略来控制,它有9种取值,对应不同的referrer策略。设置referrer policy的方式是:

  1. 通过服务端响应的报文首部行中的Content-Secure-Policy字段;
  2. 通过html的<meta name="referrer">标签;
  3. 通过<img>、<a>等的referrerpolicy属性,单独设置某个链接的referrer policy。

三、Https协议

Https协议

Keywords: Network TCP HTTP HTTPs
previousPost nextPost
已经有 1000000 个小伙伴看完了这篇推文。