传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去
6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
16位紧急指针: 标识哪部分数据是紧急数据
对于TCP报头主要讲解几个重要部分:
4位首部长度,首部,和选项之间的关系:
以保证报头和有效数据的分离
。所以他们之间的关系就是:选项是对超过首部长度的补充,而根据四位首部长度可以计算出报头总长度大小。(0<=选项<=40)。32位序号和32位确认序号分别是干嘛的?
确认应答机制
的,即主机A给主机B发送了一则消息,当收到主机B的确认信息以后,才保证主机A发的信息主机B收到了,这里就可以提出可靠性
的概念,可靠性又可以分为相对可靠性
和绝对可靠性
,网络传输数据是无法保证绝对可靠的,因为总有一条最新的消息没有被确认。TCP通过肯定的确认应答(ACK)实现可靠的数据传输。当发送端将数据发出之后会等待对短的确认应答。如果有确认应答,说明数据已经成功到达对端,反之,则数据丢失的可能性很大
TCP将每个字节的数据都进行了编号. 即为序列号。
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
重发超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍未收到确认应答,发送端将进行数据重发。
连接管理机制主要是讲三次握手和四次挥手,在之前的博文中已经有详细的讲解,小伙伴可以此处阅读->https://blog.csdn.net/weixin_45313447/article/details/117328378。
这里主要讲面试高频问题:
三次握手
:
一次,两次,四次或者更多次握手行不行?
会被攻击(洪水攻击)
。三次握手,是对通信信道的验证,让客户端和服务器都验证了接受和发送能力正常,用最小成本验证全双工
。第一次和第二次握手丢失,不用担心,因为双方都不会确立连接,最担心第三次握手丢失,客户端认为握手成功。四次握手,如果最后一次握手失败,最担心,服务器认为握手成功, 不管几次握手,都担心最后一次。最担心最后一次握手丢失,应该让客户端背锅,免得服务器是一对多的,有大量的无用连接,而浪费资源。让服务器不要出现连接建立误判的情况,减少服务器的资源浪费
。肯定不会用偶数次,更多奇数次是可以的,但是要最小成本。TCP的三次握手是否都可以携带数据
?
TCP三次握手失败,服务端会如何处理
?
RST包
响应。什么是半连接队列
?
四次挥手
:
当客户端想要断开连接时,并不是彻底和服务器断开了,是指应用层不会发数据了。客户端有四次状态,服务器有三次状态。
为什么握手是三次,而挥手时需要四次呢
?
其实在TCP握手的时候,接收端将SYN包和ACK确认包合并到一个包中发送的,所以减少了一次包的发送
。对于四次挥手,由于TCP是全双工通信,主动关闭方发送FIN请求不代表完全断开连接,只能表示主动关闭方不再发送数据了。而接收方可能还要发送数据,就不能立即关闭服务器端到客户端的数据通道,所以就不能将服务端的FIN包和对客户端的ACK包合并发送,只能先确认ACK,等服务器无需发送数据时在发送FIN包,所以四次挥手时需要四次数据包的交互。CLOSE_WAIT状态详谈
:
Server端没有发起close()操作
,这基本上是用户server端程序的问题了;通常情况下,Server都是等待Client访问,如果Client退出请求关闭连接,server端自觉close()对应的连接,当服务器接受到来自客户端FIN的断开请求时,服务器进入CLOSE_WAIT状态,并发送ACK,直到服务器想要断开连接时,发送FIN和ACK断开请求,并转变为LAST_ACK状态,如果用netstat查看有大量的CLOSE_WAIT状态说明服务器代码有BUG。TIME_WAIT状态详谈
:
确保最后一个确认报文能够到达
。进而尽快释放服务器的资源。如果没能到达,服务端就会会重发FIN请求释放连接。等待一段时间没有收到重发就说明服务的已经CLOSE了。如果有重发,则客户端再发送一次ACK信号。解决TIME_WAIT状态引起的bind失败的方法
:
在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的
使用setsockopt()
设置socket描述符的选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符:
网络风暴
”,TCP这个协议就会拖垮整个网络。所以TCP不能忽略网络上发生的事情,而无脑地一个劲地重发数据,对网络造成更大的伤害。对此TCP的设计理念是:TCP不是一个自私的协议,当拥塞发生的时候,要做自我牺牲。就像交通阻塞一样,每个车都应该把路让出来,而不要再去抢路了。
少量丢包
, 仅仅是触发超时重传
; 大量丢包
, 认为网络拥塞
;
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案。
我们知道TCP中,有确认应答机制以保证数据的可靠传输。但是是不是接受方接受到数据就立即返回ACK应答呢?如果是这样,这时候的缓冲区中接收区的数据还没能够处理,缓存区的剩余大小就是窗口大小。但是如果我们延迟一会,等待缓存区中数据被处理,那么剩余的缓存区就会大些——这就是延时应答。
ps:假设接收端缓存区大小为1M,一次接收到了500K的数据,现在缓存区中剩余大小为500。但如果我们延时一段时间,等待接受方处理了该缓存区中的数据,处理以后接受窗口变大,那么我们的剩余大小就为1M了(即:窗口大小)
窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。
等待的时间
每个操作系统中设置的等待时间是不一样的。(200ms)
是不是所有的包都可以延时应答?
具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;
捎带应答是指在同一个TCP包中即发送数据又发送确认应答的一种机制。由此,网络的利用率会提高,计算机的负荷也会减轻。不过,确认应答必须等到应用处理完数据并将作为回执的数据返回为止,才能进行捎带应答
。
那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界.
思考: 对于UDP协议来说, 是否也存在 “粘包问题” 呢?
进程终止
: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.机器重启
: 和进程终止的情况相同.机器掉电/网线断开
: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ
断线之后, 也会定期尝试重新连接。
也包括自己写TCP程序时自定义的应用层协议。
为什么TCP这么复杂? 因为要保证可靠性, 同时又尽可能的提高性能.
可靠性
:
提高性能
:
其他: