详解TCP网络协议栈的工作原理
本文分享自华为云社区《网络通信的神奇之旅:解密LinuxTCP网络协议栈的工作原理-云社区-华为云》,作者:LionLong。
一、TCP网络开发APITCP,全称传输控制协议(TransmissionControlProtocol),是一种面向连接的、可靠的、基于字节流的传输层通信协议。
1.1、TCP服务器调用的APIincludesys///1intsocket(intdomain,inttype,intprotocol);//2intbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen);//3intlisten(intsockfd,intbacklog);//4ssize_trecv(intsockfd,void*buf,size_tlen,intflags);//5intaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);//6ssize_ts(intsockfd,constvoid*buf,size_tlen,intflags);//7intclose(intfd);//8intshutdown(intsockfd,inthow);1.2、TCP客户端调用的API
includesys///1intsocket(intdomain,inttype,intprotocol);//2intbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen);//3intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);//4ssize_ts(intsockfd,constvoid*buf,size_tlen,intflags);//5ssize_trecv(intsockfd,void*buf,size_tlen,intflags);//6intclose(intfd);//7intshutdown(intsockfd,inthow);1.3、API函数的作用
(1)intsocket(intdomain,inttype,intprotocol)
在文件系统中分配一个fd,并创建TCB数据结构。
(2)intbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen)
为TCP的socket绑定本地IP地址和端口。
(3)intlisten(intsockfd,intbacklog)
将TCP置于LISTEN状态。
(4)intaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen)
从全连接队列中取出一个节点,并分配一个fd。
(5)ssize_trecv(intsockfd,void*buf,size_tlen,intflags)
在对应fd中,从读缓冲区中拷贝出数据。
(6)ssize_ts(intsockfd,constvoid*buf,size_tlen,intflags)
把fd对应的TCB数据拷贝到写缓冲区中。
(7)intclose(intfd)
准备一个FIN包,放到写缓冲区,是否fd。
(8)intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen)
准备一个SYN包,交给协议栈发送出去,等待三次握手完成后才返回。
二、TCP的三个阶段2.1TCP建立连接TCP连接的建立主要依靠socket()、bind()、listen()、connect()、accept()这几个函数。
2.1.1、TCP的三次握手示意图:

三次握手在kernel协议栈中进行,那么三次握手是在哪几个函数中发送的呢?
第一次,由connect()函数触发发起握手,也就是发送syn包到服务端;
第二次,在listen()之后accept()之前,服务器接收到syn包后发送synack包到客户端;
第三次,客户端发送ack包到服务端完成连接的建立。
TCP报头:
0|1|2|301234567890123456789012345678901+-------------------------------+-------------------------------+|SourcePort|DestinationPort|+---------------------------------------------------------------+|SequenceNumber|+---------------------------------------------------------------+|AcknowledgmentNumber|+-------+-----------+-+-+-+-+-+-+-------------------------------+|Header|Reserve|U|A|P|R|S|F|Window||Length||R|C|S|S|Y|I|||||G|K|H|T|N|N||+-------------------------------+-------------------------------+|Checksum|UrgentPointer|+---------------------------------------------------------------+|Option|+---------------------------------------------------------------+|Data|||+---------------------------------------------------------------+
SYN:即synchronous,同步。
ACK:即acknowledgement,确认。
PSH:即push,推送。
FIN:即finish,结束。
RST:即reset,重置。
URG:即urgent,紧急。
SequenceNumber:是数据包本身第一个字节的序列号。
AcknowledgeNumber:是期望对方继续发送的那个确认数据包的序列号其值一般为接收到的SequenceNumber加1。
从报文中可以看出,SYN包最重要的是将SYN位设为1,设置SequenceNumber;ACK包最重要的是将ACK位设为1,设置AcknowledgmentNumber。
半连接队列和全连接队列:
在三次握手中,Linuxkener协议栈会维护两个队列:半连接队列和全连接队列。
半连接队列(也叫SYN队列):半连接队列在第一握手中,当客户端发送SYN包到服务端时,服务端的半连接队列会加入一个节点,表示此连接处于半连接状态。
全连接队列(也叫ACCEPT队列):全连接队列在第三握手中,当客户端发送ACK包到服务端时,服务端会检查半连接队列中是否存在此连接节点(通过五元组进行查找),如果存在就将此连接节点加入全连接队列中;否则将抛弃此连接。
accpt()函数在三次握手完成后,从全连接队列中取出连接节点,为节点分配socketfd,返回到用户态。
那么,accept()函数如何知道全连接队列中有节点呢?
当三次握手完成后,全连接队列创建节点的同时会释放一个有连接接入的信号(single或信号量),这个信号决定了accept()函数是否可以从全连接队列中取节点;也决定epoll等IO多路复用器能不能检查这个连接fd是否可读。
在阻塞模式下,accept()函数一直等待信号,直到全连接队列中有节点才返回。
在非阻塞模式下,全连接队列为空accept()函数就返回-1,否则返回socketfd。
在listen()函数有,有一个backlog参数,这个参数表示的是全连接队列的大小还是半连接队列的大小呢?
随着TCP协议的不断迭代,backlog参数在不同的版本中代表的含义也不相同;它可以是半连接队列大小,也可以是全连接队列大小,也可以是半连接队列+全连接队列的大小总和。不过,效果不会有太大差异。目前版本中主要表示全连接队列的大小。
DDOS攻击:
根据三次握手原理,产生一种对服务器的攻击方式:DDOS攻击。所谓DDOS攻击,就是客户端伪造一些不存在的IP,一直发送SYN包,使服务器的半连接队列不断增大,当半连接队列的大小达到极限时,造成网络阻塞就会导致服务器无法再接受连接,从而使服务器奔溃。
2.1.2、TCP状态转换TCP状态转换图:

(1)从状态转换图看出,LISTEN状态可以通过发送SYN和数据转换到SYN_SEND状态;也就是LISTEN状态可以发送数据。
(2)SYN_SEND状态可以收到SYN,并发送SYN和ACK转换到SYN_RECV状态;也就是两个设备可以互发SYN包,建立连接。
2.2TCP传输数据TCP传输数据主要依靠s()和recv()两个函数。
使用s()函数发送数据时,返回正数不一定代表发送成功。因为s()函数仅仅只是将数据拷贝到协议栈的写缓冲区,由协议栈发送;发送过程中会经过N个网关,可能存在丢包或链路断开导致未能发送到目的地。如果要知道数据是否发送成功,需要加上确认机制(ACK)。
2.2.1、传输控制块TCB为了保证数据能正确分发,TCP使用一种TCB(传输控制块)的数据结构,把发送给不同设备的数据封装起来。这个TCB会存在整个TCP周期,知道断开连接。
一个TCB数据块包含数据发送双方对应的socket信息以及拥有存放数据的缓冲区。建立连接连接发送数据之前,通信双方必须做一个准备工作:分配内存建立TCB数据块。当双方准备好自己的socket和TCB数据结构后,就可以进入“三次握手”建立连接。
2.2.2、TCP分包TCP分包就是要传输的数据很大,超出发送缓存区剩余空间,将会进行分包;待发送的数据大于最大报文长度,TCP在传输前将进行分包。
分包在应用程序的处理一般是发送循环s(),接收方循环recv()。
2.2.3、TCP粘包及解决方案TCP粘包就是发送方发送的若干数据包到接收方接收时粘成一个包,从接收缓冲区看就是后数据包的头紧接着前数据包的尾。
常见解决方案:
(1)(推荐)应用层协议头前面添加包长度。分两次接收数据;第一次先接收包的长度,然后根据包的长度一次性读取或循环读取数据。
例如:
//ssizecount=0;ssizesize=0;while(counttcpHdr-length){size=recv(fd,buffer,buffersize,0);count+=size;}//(2)为每个包添加分隔符。在数据末尾添加分隔符,这会导致解数据可能需要有合包操作;因为分割数据包后,需要记录后一个数据包,用于与该包后面部分数据进行合并。
2.3TCP四次挥手断开连接是比建立连接和传输数据还复杂的一个过程,断开连接主要分为主动关闭和被动关闭两种。
四次挥手示意图:
需要注意的是,调用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的处理等。
(5)理解API的底层原理,以及全连接队列和半连接队列。
(6)TCP的分包场景以及TCP粘包的处理方式。
TCP通信完整过程:
华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云
推荐阅读
-
米尔电子STM32MP135开放式高实时高性能PLC控制器解决方案发布
前言随着工业数字化进程加速与IT/OT深入融合,不断增加的OT核心数据已经逐步成为工业自动化行业的核心资产,而OT层数据具备高实时、高精度、冗余度高、数据量大等等特点,如何获取更加精准的OT数据对数字化进程起到至关重要的作用,同时随着国内工业控制系统逐步进入中高端应用,更加精准的控制至关重要,因此工...
-
从编程开发角度比较电机驱动芯片:DRV8833、TB6612、A4950、L298N
这几款驱动芯片都是用于控制直流电机的常见驱动芯片,下面是它们的相同点和不同点的比较:相同点:都可以用于控制直流电机的转速和方向。都支持PWM控制方式,可以实现电机的速度调节。都提供了使能引脚,可以通过使能引脚控制电机的启停。不同点:DRV8833和TB6612是双H桥驱动芯片,A4950和L298N...
-
福音:微创肺减容术——慢阻肺/肺气肿患者治疗黑科技
深冬时节,慢阻肺/肺气肿患者又迎来了一年一度的疾病稳定状态的“大考”。慢阻肺患者老李每年到了这个季节就会心里发怵,6年前因为慢阻肺/肺大疱破裂导致张力性气胸的场景依然历历在目,经过医生们的精心治疗虽然躲过一劫,但自那以后老李的肺功能就不堪重负,几乎离不开氧气,每日可以活动的范围也是越来越小。日子一天...
-
共谋发展 | 热烈欢迎广东找大状法务科技有限公司CEO莅临小财鹅
7月27日上午,广东找大状法务科技有限公司(简称:找大状)CEO尚宏金到访小财鹅进行考察交流,小财鹅CEO林志浩对尚宏金先生的到来表示热烈欢迎。01交流座谈尚宏金先生表示找大状是国内起步最早且最有影响力的法律电商品牌之一,其主要业务是为中小企业提供从法律规范到诉讼的整体法律解决方案,利用互联网技术便...