解密Linux TCP网络协议栈的工作原理
根据四次挥手流程,可以思考一些问题: (1)传输数据过程中,网线断了之后立刻连接,TCP如何知道?可以使用异步,将网络层和业务层分离,单独处理。四次挥手FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT、CLOSING直接的转换,CLOSE_WAIT和LAST_ACK的处理等。为了保证数据能正确分发,TCP使用一种TCB(传输控制块)的数据结构,把发送给不同设备的数据封装起来。需要注意
2 TCP传输数据
TCP传输数据主要依靠send()和recv()两个函数。 使用send()函数发送数据时,返回正数不一定代表发送成功。因为send()函数仅仅只是将数据拷贝到协议栈的写缓冲区,由协议栈发送;发送过程中会经过N个网关,可能存在丢包或链路断开导致未能发送到目的地。如果要知道数据是否发送成功,需要加上确认机制(ACK)。
2.2.1、传输控制块TCB
为了保证数据能正确分发,TCP使用一种TCB(传输控制块)的数据结构,把发送给不同设备的数据封装起来。这个TCB会存在整个TCP周期,知道断开连接。 一个TCB数据块包含数据发送双方对应的socket信息以及拥有存放数据的缓冲区。建立连接连接发送数据之前,通信双方必须做一个准备工作:分配内存建立TCB数据块。当双方准备好自己的socket和TCB数据结构后,就可以进入“三次握手”建立连接。
2.2.2、TCP分包
TCP分包就是要传输的数据很大,超出发送缓存区剩余空间,将会进行分包;待发送的数据大于最大报文长度,TCP在传输前将进行分包。 分包在应用程序的处理一般是发送循环send(),接收方循环recv()。
2.2.3、TCP粘包及解决方案
TCP粘包就是发送方发送的若干数据包到接收方接收时粘成一个包,从接收缓冲区看就是后数据包的头紧接着前数据包的尾。 常见解决方案: (1)(推荐)应用层协议头前面添加包长度。分两次接收数据;第一次先接收包的长度,然后根据包的长度一次性读取或循环读取数据。 例如:
代码语言:javascript
AI代码解释
// ...
ssize count=0;
ssize size=0;
while(count<tcpHdr->length)
{
size=recv(fd,buffer,buffersize,0);
count+=size;
}
// ...
(2)为每个包添加分隔符。在数据末尾添加分隔符,这会导致解数据可能需要有合包操作;因为分割数据包后,需要记录后一个数据包,用于与该包后面部分数据进行合并。

2.3 TCP四次挥手
断开连接是比建立连接和传输数据还复杂的一个过程,断开连接主要分为主动关闭和被动关闭两种。 四次挥手示意图:

需要注意的是,调用close()不是立即完成断开,而是关闭了数据传输,进入了四次挥手阶段,TCB数据结构还没有释放。四次挥手结束才真正把TCB释放。
根据四次挥手流程,可以思考一些问题: (1)传输数据过程中,网线断了之后立刻连接,TCP如何知道? 网线掉线网卡会停止供电,再次连接后网卡恢复供电,网卡服务重启,网络连接重连。应用程序设计通过心跳包检测。 (2)服务器如何知道客户端是否宕机? 一样需要通过心跳包机制来检测。 (3)服务器如何甄别网络阻塞和宕机? 服务器发送心跳包时,不仅仅发一次,而是要发送多次的;如果是网络阻塞,那么在一定时间内一定有回复信息;如果是宕机,无论多长时间都没有客户端的回复。 (4)如果出现大量的CLOSING状态,如何处理? 出现大量CLOSING状态,基本上业务上要处理的逻辑过多,导致一直在CLOSING状态;可以使用异步,将网络层和业务层分离,单独处理。 (5)四次挥手中,为什么存在TIME_WAIT状态? 防止没有LAST_ACK或LAST_ACK丢失,导致一直重发已经不存在的socket。
总结
需要掌握TCP三次握手和四次挥手的过程,熟悉TCP状态转换。清楚什么是SYN包和ACK包。 (1)三次握手是 由客户端发起SYN,服务端收到SYN后发送SYN和ACK,客户端回复ACK;完成连接的建立。 (2)断开连接主要有主动断开和被动断开。 (3)四次挥手是 由发起方调用close(),同时发送FIN包;接收端接收到FIN包返回ACK包,接收端发送FIN包;发起方接收到FIN包返回ACK包;完成断开。 (4)理解TCP的状态转换图。LISTEN状态到SYN_RCVD状态和SYN_SEND状态,如何进入ESTABLISHED状态;四次挥手FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT、CLOSING直接的转换,CLOSE_WAIT和LAST_ACK的处理等。
更多推荐


所有评论(0)