「常见的HTTP报文头属性」
- Accpet
- 告诉服务端,客户端接收什么类型的响应
- Referer
- 表示这是请求是从哪个URL进来的,比如想在网上购物,但是不知道选择哪家电商平台,你就去问度娘,说哪家电商的东西便宜啊,然后一堆东西弹出在你面前,第一给就是某宝,当你从这里进入某宝的时候,这个请求报文的Referer就是:www.baidu.com
- Cache-Control
- 对缓存进行控制,如一个请求希望响应的内容在客户端缓存一年,或不被缓可以通过这个报文头设置
- Accept-Encoding
- 例如:Accept-Encoding:gzip, deflate(这两种都是压缩格式)
- 这个属性是用来告诉服务器能接受什么编码格式,包括字符编码,压缩形式(一般都是压缩形式)
- Host
- 指定要请求的资源所在的主机和端口
- User-Agent:告诉服务器,客户端使用的操作系统、浏览器版本和名称
- Connection
决定当前事务(三次握手和四次挥手)完成后,是否关闭网络连接。
- 持久连接,事务完成后不关闭网络连接 :Connection: keep-alive
- 非持久连接,事务完成后关闭网络连接:Connection: close
响应报文与请求报文一样,由三个部分组成(响应行,响应头,响应体)
「HTTP响应报文属性」
- Cache-Control
- 响应输出到客户端后,服务端通过该属性告诉客户端该怎么控制响应内容的缓存
- ETag
- 表示你请求资源的版本,如果该资源发生啦变化,那么这个属性也会跟着变
- Location
- 在重定向中或者创建新资源时使用
- Set-Cookie
- 服务端可以设置客户端的cookie
TCP是一个传输层协议,提供可靠传输,支持全双工,是一个连接导向的协议。
「双工/单工」
在任何一个时刻,如果数据只能单向发送,就是单工。
如果在某个时刻数据可以向一个方向传输,也可以向另一个方向反方向传输,而且交替进行,叫作半双工;半双工需要至少 1 条线路。
如果任何时刻数据都可以双向收发,这就是全双工,全双工需要大于 1 条线路。
TCP 是一个双工协议,数据任何时候都可以双向传输。
这就意味着客户端和服务端可以平等地发送、接收信息。
「TCP协议的主要特点」
- TCP是面向连接的运输层协议;所谓面向连接就是双方传输数据之前,必须先建立一条通道,例如三次握手就是建议通道的一个过程,而四次挥手则是结束销毁通道的一个其中过程。
- 每一条TCP连接只能有两个端点(即两个套接字),只能是点对点的;
- TCP提供可靠的传输服务。传送的数据无差错、不丢失、不重复、按序到达;
- TCP提供全双工通信。允许通信双方的应用进程在任何时候都可以发送数据,因为两端都设有发送缓存和接受缓存;
- 面向字节流。虽然应用程序与TCP交互是一次一个大小不等的数据块,但TCP把这些数据看成一连串无结构的字节流,它不保证接收方收到的数据块和发送方发送的数据块具有对应大小关系,例如,发送方应用程序交给发送方的TCP10个数据块,接收方的TCP可能只用收到的4个数据块字节流交付给上层的应用程序
「TCP的可靠性原理」
可靠传输有如下两个特点:
- 传输信道无差错,保证传输数据正确;
- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据;
首先,采用三次握手来建立TCP连接,四次握手来释放TCP连接,从而保证建立的传输信道是可靠的。
其次,TCP采用了连续ARQ协议(回退N(Go-back-N);超时自动重传)来保证数据传输的正确性,使用滑动窗口协议来保证接方能够及时处理所接收到的数据,进行流量控制。
最后,TCP使用慢开始、拥塞避免、快重传和快恢复来进行拥塞控制,避免网络拥塞。
报文段TCP虽面向字节流,但传送的数据单元为报文段
报文段 = 首部 数据2部分
TCP的全部功能体现在它首部中各字段的作用
❝首部前20个字符固定、后面有4n个字节是根据需而增加的选项故 TCP首部最小长度 = 20字节❞
「端口」:
源端口号和目地端口各占16位两个字节,也就是端口的范围是2^16=65535
另外1024以下是系统保留的,从1024-65535是用户使用的端口范围
「seq序号」:占4字节,TCP连接中传送的字节流中的每个字节都按顺序编号。
例如:一段报文的序号字段值是107,携带的数据是100个字段,下一个报文段序号从107 100=207开始。
「ack确认号」:4个字节,是期望收到对方下一个报文段的第一个数据字节的序号。
例如:B收到A发送的报文,其序号字段是301,数据长度是200字节,表明B正确收到A发送的到序号500为止的数据(301 200-1=500),B期望收到A下一个数据序号是501,B发送给A的确认报文段中把ack确认号置为501。
「数据偏移」:头部有可选字段,长度不固定,指出TCP报文段的数据起始处距离报文段的起始处有多远。
「保留」:保留今后使用的,被标为1。
「控制位」:由8个标志位组成。每个标志位表示一个控制功能。
其中主要的6个:
- 「URG紧急指针标志」,为1表示紧急指针有效,为0忽略紧急指针。
- 「ACK确认序号标志」,为1表示确认号有效,为0表示报文不含确认信息,忽略确认号字段,上面的确认号是否有效就是通过该标识控制的。
- 「PSH标志」,为1表示带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将该报文段交给应用程序,而不是在缓冲区排队。
- 「RST重置连接标志」,重置因为主机崩溃或其他原因而出现错误的连接,或用于拒绝非法的报文段或非法的连接。
- 「SYN同步序号」,同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。。
- 「FIN终止标志」,用于释放连接,为1时表示发送方没有发送了。
「窗口」:滑动窗口大小,用来告知发送端接收端缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。
「校验和」:奇偶校验,此校验和是对整个的TCP报文段(包括TCP头部和TCP数据),以16位进行计算所得,由发送端计算和存储,接收端进行验证。
「紧急指针」:只有控制位中的URG为1时才有效,指出本报文段中的紧急数据的字节数。
「选项」:其长度可变,定义其他的可选参数。
粘包与拆包TCP是面向字节流的协议,把上层应用层的数据看成字节流,所以它发送的不是固定大小的数据包,TCP协议也没有字段说明发送数据包的大小。
而且TCP不保证接受方应用程序收到的数据块和发送应用程序发送的数据块具有对应的大小关系
比如发送方应用程序交给发送方TCP 10个数据块,接受方TCP可能只用了4个数据块就完整的把接受到的字节流交给了上层应用程序。
TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题
「TCP粘包/拆包解决策略」
由于TCP无法理解上一层的业务数据特点,所以TCP是无法保证发送的数据包不发生粘包和拆包,这个问题只能通过上层的协议栈设计来解决,解决思路有一下几种:
- 消息定长:每个发送的数据包大小固定,比如100字节,不足100字节的用空格补充,接受方取数据的时候根据这个长度来读取数据
- 消息末尾增加换行符来表示一条完整的消息:接收方读取的时候根据换行符来判断是否是一条完整的消息,如果消息的内容也包含换行符,那么这种方式就不合适了。
- 将消息分为消息头和消息尾两部分,消息头指定数据长度,根据消息长度来读取完整的消息,例如UDP协议是这么设计的,用两个字节来表示消息长度,所以UDP不存在粘包和拆包问题。
「第一次握手」:
客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=J,保存在TCP首部的序列号字段里,指明客户端打算连接的服务器的端口,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT状态,等待服务器端确认。
「第二次握手」:
服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=J 1,随机产生一个序号值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
「第三次握手」:
客户端收到确认后,检查ack是否为J 1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K 1,并将该数据包发送给服务器端,服务器端检查ack是否为K 1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
「上面写的ack和ACK,不是同一个概念:」
- 小写的ack代表的是头部的确认号Acknowledge number, 缩写ack,是对上一个包的序号进行确认的号,ack=seq 1。
- 大写的ACK,则是我们上面说的TCP首部的标志位,用于标志的TCP包是否对上一个包进行了确认操作,如果确认了,则把ACK标志位设置成1。
「TCP为什么三次握手而不是两次握手」
- 为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
- 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
「《计算机网络》中是这样说的:」
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
❝
在书中同时举了一个例子,如下:
❞
假如client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server,本来这是一个早已失效的报文段,但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。
于是就向client发出确认报文段,同意建立连接,假设不采用「三次握手」,那么只要server发出确认,新的连接就建立了,由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。
但server却以为新的连接已经建立,并一直等待client发来数据,这样,server的很多资源就白白浪费掉了。
采用「三次握手」的办法可以防止上述现象发生,例如刚才那种情况,client不会向server的确认发出确认,server由于收不到确认,就知道client并没有要求建立连接。
「什么是半连接队列」
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为「半连接队列」。
当然还有一个「全连接队列」,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
补充一点关于「SYN-ACK 重传次数」的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
「三次握手过程中可以携带数据吗」
其实第三次握手的时候,是可以携带数据的,也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据没啥毛病。
四次挥手