【Linux篇】详解TCP/UDP传输层协议:全面拆解三次握手、四次挥手及可靠性机制
个人主页孙同学_文章专栏Liunx关注我分享经验助你少走弯路文章目录传输层协议UDP传输层协议TCPTCP协议TCP协议的格式可靠性的本质TCP报头当中的标志位问题三次握手四次挥手确认应答机制ACK机制超时重传机制再谈三次握手再谈四次挥手滑动窗口快重传 vs 超时重传流量控制拥塞控制延迟应答捎带应答粘包问题TCP连接异常情况TCP小结传输层协议UDP传输层传输层负责把数据从一台主机发送到另一台主机。我们之前的write和read接口并不是直接把数据发送到网络中write接口只是把缓冲区中的数据拷贝到UDP的缓冲区中至于数据怎么发发多少由操作系统自己决定。端口号端口号标识了一个主机上进行通信的不同应用程序。当我们的底层一旦收到了报文ip地址表明了我要把报文发送给那台主机端口号用来把报文交付给上层的哪一个应用。在TCP/UDP协议中用“源ip“源端口号”“目的ip”,“目的端口号”“协议号”这样五元组来表示一个通信可以通过netstat -n查看端口号的划分范围认识知名端口号协议本质是结构体UDP协议的格式UDP的特点udp的传输过程类似于寄信。无连接知道对端的ip和端口号就能进行传输不需要建立连接。不可靠没有确认机制没有重传机制。如果因为网络故障该段无法发送给对方UDP协议层也不会给应用层返回任何错误信息。面向数据报不能灵活的控制读写数据的次数和数量。面向数据报应用层交给UDP多长的报文UDP原样发送既不会拆分也不会合并。用UDP传输100字节数据如果发送端调用一次sendto发送100个字节那么接收端也必须调用一次recvfrom,接受100个字节而不能一直循环的调用10次recvfrom,每次接受10字节。UDP的缓冲区UDP没有真正意义上的发送缓冲区因为没必要调用sendto就直接发给内核由内核将数据传给网络层协议进行后续的传输动作。UDP具有接受缓冲区但这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致如果缓冲区满了再到达缓冲区的数据就会被丢弃。UDP的socket既能收也能写这个概念叫做全双工。UDP使用注意事项我们注意到UDP协议的首部中有一个16位的最大长度也就是说一个UDP能传输的最大数据长度是64k(包含UDP首部)。然而64k在当前互联网时代是一个非常小的数字。如果我们需要传输的数据超过了64k就需要在应用层手动的分包多次发送并在接收端手动拼装。基于UDP的应用层协议NFS:网络文件系统TFTP:简单文件传输协议DHCP:动态主机配置协议BOOTP:启动协议用于无盘设备启动DNS:虚名解析协议在操作系统内部一定可能会同时存在大量的报文所以操作系统就要管理这些报文。如何管理呢先描述再组织下图为描述报文的结构体对象sk_buff传输层协议TCPTCP协议“TCP”全称为传输控制协议要对数据的传输进行一个详细的控制。TCP协议的格式1.标准问题a, 如何将报头和有效载荷进行分离TCP报头的长度是20字节一般而言TCP报头也是定长报头。TCP的报头是20字节的定长报头再加上选项选项一般为0.问题是TCP把20个字节读完后到底有没有选项是怎么知道的选项是多少字节又是怎么知道的所以在TCP的标准报头20字节里面存在一个4为首部长度的字段4个比特位4位首部长度也就是报头长度包含了选项。规定约定4位首部在计算式的基本单位是4字节。如果4位首部长度是0那么报头长度就是0*4字节。如果4位首部长度是15那么报头长度就是15*4 60字节。因为标准报头是20字节所以4位首部长度的最小值就是20字节所以4位首部长度最小为5。结论因为TCP的报头长度的基本单位是4字节所以就决定了不管TCP的选项有多少TCP的报头肯定是能整除4字节的。报头和有效载荷是如何分离的呢答案是先无脑的读取前20字节因为20个字节是标准的必须得有在提取4位有效长度提取完4位有效程度后乘以4再减去20就是选项的长度剩下的就是有效载荷了。这里怎么只有报头大小没有报文大小TCP是面向字节流的在操作系统内部即便你发了多个TCP数据段他也区分不清楚是不是一个完整的报文所以对于TCP来讲不需要来设置一个报文的字段。b. 如何将自己的有效载荷交付给上一层TCP的报头里面有16位的源端口号16位的目的端口号当在传输层中收到了一个TCP报文它肯定也是一个sk_buff,当收到报文时提取目的端口号根据目的端口号查进程就能把报文交给对应的进程了可靠性的本质1.具有应答可以保证历史消息的可靠性。2.在通信的过程中最新的报文永远没有应答最新的可靠性无法保证。如果保证可靠性在TCP中的机制叫做确认应答机制。客户端给服务器发消息服务器收到消息后要应答。服务器给客户端发消息客户端要给服务器做应答不对应答做应答在TCP中如果客户端连续的发送几次请求那么服务端也需要有连续的应答当服务端对于某个请求没有应答时那么是怎么解决那么请求丢失了呢答案是32位序号和32位确认序号32位序号是我们发送报文时对报文带的序号那么应答回复的序号是确认序号发送序号数1。发送请求时的32位序号是100应答的确认序号是101代表着101之前的报文全部收到指定报文的序号之前的所有信息已经全部收到。下一次发送从该信号开始报文将来按指定序号发但是不一定按指定顺序收这叫做乱序问题是不可靠的一种问题。所以在今天我们的TCP不会有乱序问题因为有序号我们可以按照序号的大小进行排序解决乱序问题。现在我们就清楚了为什么TCP中要有32位的序号可以确认应答。可以解决乱序问题。客户端和服务器进行数据交互的时候交互的不是箭头而是报文。要么是裸的报头要么是报文数据为什么要有两个序号服务器不仅仅做应答还会捎带应答。既需要给对方报文做确认同时自己的报文也要有序号!16位窗口大小接受缓冲区剩余空间的大小发送端如何尽早知道接收端的接受能力呢一台主机的接受能力由对方的接受缓冲区决定由于对方的接受缓冲区中也可能有数据所以对端的接受能力由对方接收缓冲区剩余空间大小决定。我们要保证给对方发送数据按量发送和按需发送我们就必须得知道对方剩余缓冲区的大小。流量控制问题我们把按照对方的接受能力来动态调整自己发送数据速度的机制叫做流量控制。流量控制主要解决的是效率问题。TCP报头当中的标志位问题TCP的报头里面有保留的6个位除了保留的6个位之外还有6个标志位。三次握手SYN(你要做我女朋友吗)SYnACK(好啊什么时候开始呢) ACK(就现在)前两次握手不能携带数据,因为3次握手没有完成SYN,SYNACK只有报头。三次握手已经可以进行双方接受能力的协商了。SYN标志位同步标志位通常表示建立连接握手过程的标志位。表明自己的报文类型是一个建立连接相关的报文ACK标志位确认号是否有效。说人话就是表明报文是一个应答报文。ACK标志位几乎常被设置为1的因为报文大部分是应答报文或者报文数据捎带应答。四次挥手四次挥手客户端要和服务器断开连接服务器也要向客户端断开连接。本质是两个互相断开连接的请求被对方可靠收到了加上应答是4次所以叫4次挥手因为断开连接要经过双方的同意。本质是关闭全双工。FIN标志位通知对方本段要关闭了我们称携带FIN标识的为结束报文段 表明这是一个断开连接的报文。PSH标志位提示接收端应用程序立刻从TCP缓冲区中把数据读。即要求接收端把接收缓冲区的数据尽快向上交付。RST标志位通信的过程中双方连接出现了任何问题都可以对链接进行重置。TCP是保证可靠性的通信之前要先建立连接而建立连接一定会成功吗答案是不一定客户端发出ACK才算三次握手而服务端收到ACK才算三次握手所以这之间存在一定的时间差如果客户端发出ACK,ACK丢了的话客户端会认为自己的建立连接是成功的而服务端会认为三次握手都没建立成功所以就造成了客户端和服务器在建立连接是否成功不一致的问题。客户端认为自己的连接建立好了就有可能给服务器发送数据然而服务器端的连接并没有建立好所以服务端就向客户端发送了RST。URG标志位紧急指针是否有效。要配合TCP报头中的16位紧急指针URG标志位如果置为0,代表16位紧急指针无效。URG标志为1标识16为紧急指针有效。紧急数据并不属于常规数据叫做带外数据。16位紧急指针表示的是再当前报文的有效载荷中特定偏移量处有紧急数据。紧急数据只有一个字节用来设置各种状态码。确认应答机制ACK机制TCP每个字节的数据都进行了编号即为序号。每一个ACK都带有对应的确认序号。意思是告诉发送者我已经收到了哪些数据下一次发送数据的时候应该从哪处发。超时重传机制如何理解丢包问题丢包的情况就两种情况一主机A给主机B发送数据主机A携带的报文真的丢了。情况二主机A给主机B发送消息数据主机B真的收到了但是应答丢了。重新理解应答报文发送方没有收到应答ACK意味着什么不能意味着丢包他只能意味着数据可能丢失了对方可能没收到。意味着我们无法百分之百确定接收端收到报文也就是无法保证可靠性。没有收到ACK,要么数据丢要么应答丢。客户端收不到对应的应答只能能带特定的时间间隔如果在特定的时间间隔收不到应答就判定报文丢失。收不到应答并且超时我们才判定它丢包了。等待特定时间间隔本质是在等应答。这里会衍生出一个问题主机B收到的报文重复了主机B可以甄别出来报文时重复的因为我们的报文是有序号的。所以序号的作用1确认应答 2去重这个时间间隔是多长呢因为网络的网速是变化的所以这个时间间隔也应该是变化的。TCP 为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。Linux 中(BSD Unix 和 Windows 也是如此), 超时以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是 500ms 的整数倍。如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传。如果仍然得不到应答, 等待 4 * 500ms 进行重传. 依次类推, 以指数形式递增。累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接。再谈三次握手当我们想要通信的时候我们首先必须得先进行三次握手connect:发起三次握手三次握手的具体过程由客户端操作系统自己完成。accept:不参与三次握手三次握手由server os和client os自动完成。❗️为什么要有三次握手三次握手是以最短的方式验证全双工。三次握手真的是三次握手吗2. 以最小成本百分之百确认双方通信意愿。再谈四次挥手断开连接的本质建立双方断开连接的共识。为什么是四次挥手因为四次挥手是在全双工下以最短次数建立双方断开连接的请求。如果客户端把连接关掉了服务器还能给客户端发消息吗如果客户端已经退出或者关闭,服务端就是不管,那么服务端就会一直处在一个close_wait的状态,而close_wait会依旧占用文件描述符,连接也没有释放我们自己作为服务器端,我们把文件描述符用完了就必须得关掉,如果不关,服务端有可能会处在一个close_wait的状态,close_wait就会一直在占用文件描述符,如果我们的服务器大量的代码处理了文件描述符就是不关,服务器一直处于close_wait,文件描述符一直被占用,建立的连接依旧都没有被释放,如果一个东西是有用的并且是有限的,那么他就是资源,比如内存,文件描述符本质是当前服务器进程的数组下标,如果我们把文件描述符不关,最后就会导致可用的文件描述符越来越少,这个过程我们叫做文件描述符泄漏问题.当客户端收到FIN发出ACK就算客户端的四次挥手完成了,而客户端理论上四次挥手一旦完成.客户端的连接就应该直接关闭,可事实上客户端的连接并没有直接关闭(close),也就是说主动断开连接的一方把最后一个ACK发送完成不能立马进入close状态,它必须得等待一定的时间,过后然后把自己才能处于close的状态.主动断开连接的一方,要进入一个状态,TIME_WAIT状态,即便是四次挥手完成。等待两个MSL(最大报文的存活时间)时间后才能回到CLOSED状态。为什么是2倍的MSL ?MSL 是 TCP 报文的最大生存时间, 因此 TIME_WAIT 持续存在 2MSL 的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);同时也是在理论上保证最后一个报文可靠到达(假设最后一个 ACK 丢失, 那么服务器会再重发一个 FIN. 这时虽然客户端的进程不在了, 但是 TCP 连接还在, 仍然可以重发 LAST_ACK);为什么要TIME_WAIT ?等待网络消散。避免短时间内源端口目的端口的复用导致历史报文被发送。保证最后一个ACK被对方收到。哪一方发出ACK就进入TIME_WAIT了在TIME_WAIT期间如果这个ACK丢了发送方要再次FIN没得到应答超时重传。在TIME_WAIT期间没有再次收到FIN就认为对方收到ACK了。如果我们今天既要服务器进入TIME_WAIT又要服务器重启呢用setsocketpot可以设置对套接字所对应的操作选项。使用 setsockopt()设置 socket 描述符的 选项 SO_REUSEADDR 为 1, 表示允许创建端口号相同但 IP 地址不同的多个 socket 描述符.重连之后如何避免收到历史报文序号。当建立新连接后旧的报文的序号和新的报文的序号不一定配得上如果data所对应的序号和我们期望收到的序号不匹配的时候这个报文是可以被丢弃的。滑动窗口我们已经了解了确认应答的策略对每一个发送的数据段都要有一个ACK来确认应答。收到ACK后再发送下一个数据段。这样做有一个缺点就是性能比较差尤其是数据的往返时间比较长的时候。既然这样一发一收的效率比较低那么我们一次发送多条数据就可以大大提高性能了。发送方一次向对方发送多少数据由什么决定答案是滑动窗口滑动窗口是一次主机A向主机B发送数据的最大值。滑动窗口是TCP发送缓冲区的一部分滑动窗口将我们的缓冲区分成了三部分已发送已确认之后不就是这部分数据无效了这部分空间可以被利用了所以对于网络通信而言我们对于已发送已确认这部分的缓冲区不用刻意的去清空缓冲区而只需要将对应的数据无效即可只要它在滑动窗口的左侧就认为它是无效的了。序号在发送的轮次当中数字是一次增大的那么也就意味着宏观上滑动窗口整体未来要向右滑动。所以滑动窗口的本质让start和end下标增加细节一滑动窗口的大小由谁决定答案是对方的接收能力收到的报文中的win大小滑动窗口的本质是流量控制的具体实现方法如何调整滑动窗口的大小start 报文确认序号end 报文确认序号 win(窗口大小)问题1.滑动窗口可以向左滑动吗不会2.滑动窗口可以变大吗可以变小吗可以为0吗都可以所有的这些变化都取决于对方的接收能力3.如果滑动窗口太大丢包了怎么办滑动窗口会不会跳过报文进行应答a.最左侧数据段丢失b.中间报文丢失c.最右侧报文丢失实际丢包的情况肯定是这三种的自由组合最左侧丢失①情况一(1)最左侧报文数据真的丢了滑动窗口最左侧不变。当某一段报文段丢失之后, 发送端会一直收到 1001 这样的 ACK, 就像是在提醒发送端 “我想要的是 1001” 一样;如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;这个时候接收端收到了 1001 之后, 再次返回的 ACK 就是 7001 了(因为 2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;这种机制被称为 “高速重发控制”(也叫 “快重传”).快重传 vs 超时重传快重传的条件必须收到3个同样的确认应答 超时重传的条件超时了如果我们在TCP中收不到3个同样的确认应答呢比如说就只发送了2个报文其中有一个报文丢了那么就收不到3个同样的确认应答此时就需要超时重传了所以真正的重传机制是快重传和超时重传之间进行互相配合超时重传是给快重传进行兜底的。tcp发出暂时没有应答的报文的时候必须让对应的报文保存起来以方便后续的重传保存在哪里如何理解保存在滑动窗口中保存其实就是窗口不要移动当收到了相应报文的应答时滑动窗口向右移动就相当于丢弃数据。所以超时重传和快重传的底层支持是滑动窗口(2)最左侧报文对应的应答丢了滑动窗口正常工作中间报文丢失依据确认序号的规定所以中间报文丢失也就变成了最左侧报文丢失。最右侧报恩丢失也会转化成最左侧丢失。所以滑动窗口不会跳过报文进行应答因为确认序号的定义决定的。问题如果没有收到应答滑动窗口的位置会不会更新不会4.滑动窗口一直向右会不会溢出不会可以把缓冲区理解成为一个char类型的数组可以把char类型的数组想象成一个环形区域。流量控制如果主机A给主机B发送消息接收端处理数据的速度是有限的如果发送端发送数据的速率比较快导致接收端的缓冲区被打满这个时候如果发送端继续发送数据就有可能出现丢包继而引起丢包重传等一系列的连锁反应。因此TCP支持根据接收端的处理能力来决定发送端的发送速度这个机制叫做流量控制(Flow Control)。接收端将自己可以接收缓冲区剩余空间的大小放入TCP首部中的“窗口大小”字段通过ACK端通知发送端窗口大小字段越大说明网络的吞吐量越高接收端一旦发现自己的缓冲区满了就会将窗口大小设置成一个更小的值发送给发送端。发送端接收到这个窗口之后就会减慢自己的发送速度。如果接收端的缓冲区满了就会将窗口设置为0这时发送方不再发送数据但是需要定期发送一个窗口探测数据段。使接收端把窗口大小告诉发送端。那么问题来了, 16 位数字最大表示 65535, 那么 TCP 窗口最大就是 65535 字节么?实际上, TCP 首部 40 字节选项中还包含了一个窗口扩大因子 M, 实际窗口大小是 窗口字段的值左移 M 位;拥塞控制同样是丢包但是丢包多和丢包少对应的结论是不同的。TCP保证可靠性不仅仅保证了双方主机的问题还考虑了网络本身的问题。大面积的丢包发送端判定网络拥塞问题如果是大量报文判定拥塞了不敢立即重发。如果立即重发那么势必会增加网络的压力负载会让网络变得更加拥堵。多个c,多个s,都要走同一个网络。如果发生了网络拥堵在不清楚网络状态的情况下大量的客户端继续再往网络里面发送数据就会造成网络拥堵的加剧。所以就要拥塞控制拥塞控制会让发送端的多台主机都采用这种策略。TCP引入慢启动机制采用指数级增长的方式先发送一个报文如果收到了应答再发送两个再发送四个以此类推。像上面这样的拥塞窗口增长速度, 是指数级别的. “慢启动” 只是指初使时慢, 但是增长速度非常快。那么问题来了发送多少数据不是由滑动窗口决定吗滑动窗口 对方的接收能力为了支持拥塞控制算法我们提出一个新的概念这个概念叫做拥塞窗口拥塞窗口一个临界值值以内网络较大概率不拥塞值以上网络可能拥塞。我们把拥塞窗口就看作一个整形变量如果我们发送的数据量超过了拥塞窗口的大小那么就可能会导致网络拥塞。如果发送数据量小于拥塞窗口的大小我们就会认为有较大的概率不会导致网络拥塞。网络是变化的就决定这个拥塞窗口是变化的。所以我们此时要对滑动窗口有一个新的认识滑动窗口 min 对方win ,拥塞窗口 )谁小谁是主要矛盾既考虑了网络拥堵问题又考虑了对方接收能力的问题为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍。此处引入一个叫做慢启动的阈值。当 拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长。首次的ssthresh值规定为16所以我们在实际通信的时候拥塞窗口在增加我们发送的数据量一定在增加吗答案是不一定所以当我们的拥塞窗口和真实的网络通信时正常发送数据基于对方的接收能力进行流量控制在我们正常发送消息的同时我们的拥塞窗口的数字也一直在更新。加法增大的本质就是给我们探测新的拥塞窗口的值本质是为了探测。在发送数据的时候如果突然发生了网络拥塞。我们要将指数变为1重新开始慢启动除了支持慢启动本质上也是重新开始探测网络健康在此处发生拥塞了本质不就是探测出来了当前网络的拥塞窗口。新的ssthresh值规定为上一次网络拥塞时的数字除以2这也叫做乘法减小拥塞窗口在线性探测的过程中会一直增大吗逻辑上讲它就该一直增大表明我在网络通信的过程中我的网络特别的好。但是理论上这个数字不会一直增大。单位时间内我们究竟能发多少数据量本质是由带宽硬件决定的即便网再好我们的带宽决定了我们的上限。所以拥塞窗口这个数字再怎么增大它增大到一定的值就不会增大了。当 TCP 开始启动的时候, 慢启动阈值等于窗口最大值;少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;当 TCP 通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;拥塞控制, 归根结底是 TCP 协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案.延迟应答接收方等一会就有可能给发送方通告一个更大的窗口。就有较大概率更新出一个滑动窗口。下次发送消息时就能发出更多的消息。这种通过延迟给对方通告更大窗口从而在较大概率上提高我们发送效率的机制叫做延迟应答。所以延迟应答是解决TCP效率问题。捎带应答捎带应答就是接收方将确认信息ACK顺便搭载在即将要发送给对方的业务数据包中实现“一包两用”从而减少网络包数量并提升传输效率。面向字节流创建一个 TCP 的 socket, 同时在内核中创建一个发送缓冲区和一个接收缓冲区;调用 write 时, 数据会先写入发送缓冲区中。如果发送的字节数太长, 会被拆分成多个 TCP 的数据包发出。如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去。接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区。然后应用程序可以调用 read 从接收缓冲区拿数据。另一方面, TCP 的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工。粘包问题问题当我们在读取报文的时候。我们并不清楚我们读到的是不是一个完整的报文有可能读到第一次请求里面的后半部分报文和第二次请求中的前半部分报文这叫做TCP中的粘包问题。那么如何避免粘包问题呢归根揭底就是一句话明确两个包之间的边界如何明确协议 序列和反序列化对于定长的包保证每次都按固定大小读取即可。对于变长的包可以在包头的位置约定一个包总长度的字段从而就知道的了包的结束位置。对于变长的包在包与包之间使用明确的分隔符应用层协议是程序员自己定义的只要保证分隔符不和正文冲突即可TCP连接异常情况进程终止进程终止会释放文件描述符仍然可以发送FIN,和正常关闭没有什么区别。机器重启和进程终止的情况相同。机器掉电/网线断开:接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP 自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放。TCP小结可靠性校验和序列号按序到达确认应答超时重发连接管理流量控制拥塞控制提高性能滑动窗口快重传延迟应答捎带应答其他定时器超时重传计时器保活定时器TIME_WAIT定时器等TCP vs UDP我们说了 TCP 是可靠连接, 那么是不是 TCP 一定就优于 UDP 呢? TCP 和 UDP 之间的优点和缺点, 不能简单, 绝对的进行比较。TCP 用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景。UDP 用于对高速传输和实时性要求较高的通信领域, 例如, 早期的 QQ, 视频传输等. 另外 UDP 可以用于广播。归根结底, TCP 和 UDP 都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定。用 UDP 实现可靠传输(经典面试题)引入序列号保证数据顺序引入确认应答确保对端收到了数据引入超时重传如果隔一段时间没有应答就重新发送数据。引入去重机制接收方根据序列号判断如果收到已存在的ID则丢弃解决重复问题。…源码分析sock中有struct sk_buff_hand sk_reader_queue和 sk_buff_hand sk_reader_queue sk_write_queue这就是tcp中的发送缓冲区和接受缓冲区UDP是直接读取一个一个的reader_queue的这也是UDP是面向数据报的原因struck inet_sock里面有原ip目的ip源端口目的端口等信息inet_connection_sock里面又包含了request_sock_queue请求队列把请求建立成功后的连接结构体放在这里面struct tcp_sock里面包含了inet_connection_sock里面又包含了inet_sock里面又包含了sock,sock就是未来指针指向的。未来建立三次连接只需要创建一个tcp_sock结构就可以了一旦创建了tcp_sock结构后面的结构体就全都有了。当前的指针指向的是sock如果我们想要访问inet_sock只需要把该指针(sock)强转为inet_sick,其他的也类似。用c语言进行struct的嵌套就能实现c语言版本的多态了。udp_sock中保存的是inet_sock里面保存的是ip地址端口号也就是我们的网络信息而inet_sock里包含了sock而sock里面有对应的接受和发送队列(sk_receive_queue,sk_write_queue)而sock将来被struct_file指向而udp中没有inet_connection_sock即没有连接相关的东西如果对你有帮助欢迎点赞 ⭐️收藏 关注