96SEO 2026-02-19 11:13 9
包传给接收端主机每发送一次数据就「累加」一次该「数据字节数」的大小。

用来解决网络包乱序问题。
确认应答号指下一次「期望」收到的数据的序列号发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。
用来解决丢包的问题。
时表示希望建立连接并在其「序列号」的字段进行序列号初始值的设定。
FIN该位为
时表示今后不会再有数据发送希望断开连接。
当通信结束希望断开连接时通信双方的主机之间就可以相互交换
层是「不可靠」的它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
为了能够可靠传递就需要上层传输层的
是一个工作在传输层的可靠数据传输的服务它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
是面向连接的、可靠的、基于字节流的传输层通信协议。
面向连接就是指一定是「一对一」的连接不像
报文有序即使后面的报文先到达也不会给应用层而是等待同时重复报文会自动丢弃。
地址和端口号组成序列号之前已经学习就是解决乱序问题窗口大小就是用来流量控制。
连接都要占用一定内存操作系统的内存是有限的如果内存资源被占满后会发生
地址寻找主机传输层通过端口来寻址找到同一计算机中通信的不同应用程序。
端口号就是为了区分同一主机上不同的应用程序的数据包。
所以说TCP、UDP
最后把报文发送给服务端这次报文可以携带客户到服务端的数据之后客户端处于
首要原因是为了防止旧的重复连接初始化造成混乱。
如果客户端发了旧的
两次握手就无法阻止历史连接因为此时服务端没有中间状态给客户端来阻止历史连接导致服务端可能建立一个历史连接造成资源浪费。
要解决这种现象最好就是在服务端发送数据前也就是建立连接之前要阻止掉历史连接这样就不会造成资源浪费而要实现这个功能就需要三次握手。
都必须维护一个「序列号」因为依靠序列号接收方可以去除重复数据接收方可以以此按序接收通过序列号来标识已经被接收到的报文通过
报文已被服务端成功接收同理服务端发送「初始序列号」给客户端的时候依然也要得到客户端的应答回应这样一来一回才能确保双方的初始序列号能被可靠的同步。
通过四次握手也就是两次互相发
如之前所说只有两次握手那么就会有历史连接因为服务端不确定客户端是否收到
防止历史报文被下一个相同四元组连接接收主要安全性防止相同序列号
之后客户端传资源超时服务端断电重启那么久会在重启的时候建立失效进而导致服务端回发
CLOSED。
然后如果再次建立连接此时序列号是一样的而上一次连接发送的那个报文正好抵达服务端那么现在的序列号没变就会导致数据传递是有效的那么服务端就会正常接收导致数据产生混乱。
如果序列号不一样那么第二次连接生效的序列号就发生了改变也就不会导致接收成功了会把该报文直接丢弃。
tcp_syn_retries内核参数控制这个参数是可以自定义的默认值一般是
所以如果第二次丢失对第一次握手的确认报文就没有了客户端就会触发超时重传机制重传
的确认报文所以当第三次握手丢失了如果服务端那一方迟迟收不到这个确认报文就会触发超时重传机制重传
应答久而久之就会占满服务端的半连接队列使得服务端不能为正常用户服务。
ACK因此通常被称为四次挥手。
这里一点需要注意是主动关闭连接的才有
时仅仅表示客户端不再发送数据了但是还能接收数据。
服务端收到客户端的
应答报文而服务端可能还有数据需要处理和发送等服务端不再发送数据时才发送
函数关闭连接指定了只关闭发送方向而接收方向并没有关闭那么意味着主动关闭方还是可以接收数据的。
此时如果主动关闭方一直没收到第三次挥手那么主动关闭方的连接将会一直处于
函数关闭连接。
此时内核是没有权利替代进程关闭连接必须由进程主动调用
Lifetime报文最大生存时间它是任何报文在网络上存在的最长时间超过这个时间报文将被丢弃。
因为
网络中可能存在来自发送方的数据包当这些发送方的数据包被接收方处理后又会向对方发送响应所以一来一回需要等待
这其实是相当于至少允许报文丢失一次。
而起始计算的时间是从客户端接收到
防止历史连接中的数据被后面相同四元组的连接错误的接收保证「被动关闭连接」的一方能被正确的关闭。
0。
初始序列号是客户端和服务端会各自生成的一个随机数可被视为一个
小时。
两者并不是无限递增的会发生回绕为初始值的情况这意味着无法根据序列号来判断新老数据。
报文就会抵达客户端该序列号又刚好落在客户端接收窗口内就会使得客户端正常接收导致数据错乱。
时长这个时间足以让两个方向上的数据包都被丢弃使得原来连接的数据包在网络中都自然消失再出现的数据包一定都是新建立连接所产生的。
能让被动关闭方接收从而帮助其正常关闭。
如果客户端主动关闭方最后一次
资源、线程资源等第二是占用端口资源端口资源也是有限的一般可以开启的端口为
PORT」都一样的服务端发起连接了但已经被使用的端口还是可以继续对另一个服务端发起连接的。
四元组来定位客户端端口一样并不影响连接
字节字段用来保存最近一次接收对方发送到达数据的时间。
由于引入了时间戳我们在前面提到的
net.ipv4.tcp_max_tw_buckets这个值默认为
so_linger,sizeof(so_linger));如果服务端要避免过多的
状态的连接就永远不要主动断开连接让客户端去断开由分布在各处的客户端去承受
状态是主动关闭连接方才会出现的状态也就是说现在是服务器主动断开很多
Keep-Alive都是由服务端主动关闭连接那么此时服务端上就会出现
响应后服务端也会主动关闭连接。
在服务端主动关闭连接的情况下只要调用一次
Keep-Alive。
**任意一方没开都会导致服务端在处理完一个
长连接上最大能处理的请求数量当超过最大限制时就会主动关闭连接。
如果达到这个参数设置的最大值时则
状态是「被动关闭方」才会有的状态而且如果「被动关闭方」没有调用
状态的连接的时候通常都是代码的问题这时候我们需要针对具体的代码一步一步的进行排查和定位主要分析的方向就是服务端为什么没有调用
保活机制会开始作用每隔一个时间间隔发送一个探测报文该探测报文包含的数据非常少如果连续几个探测报文都没有得到响应则认为当前的
保活时间对端主机宕机并重启此时对端可以响应但是连接已经失效了只会返回
但这个时间是很长的所以可以自己定义一个时间来实现这个功能web
秒内都没有再发起新的请求定时器的时间一到就会触发回调函数来释放该连接。
报文后续的挥手过程也都是在内核完成并不需要进程的参与所以即使服务端的进程退出了还是能与客户端完成
会被放在已排队等候的其他已接收的数据之后这就意味着服务端需要处理这种异常情况因为
客户端是可以自己连自己的形成连接TCP自连接也可以两个客户端同时向对方发出请求建立连接TCP同时打开这两个情况都有个共同点就是没有服务端参与也就是没有
是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。
指的是数据发送时刻到接收到确认的时刻的差值也就是包的往返时间。
超时重传时间是以
如果已经重发再度出发超时重发这时候超时的时间就会加倍。
两次超时就说明网络环境差不宜频繁反复发送。
但是虽然解决了超时时间的问题还是存在着重传时是重传一个还是重传所有的问题。
为了解决不知道该重传哪些
的东西它可以将已收到的数据的信息发送给「发送方」这样发送方就可以知道哪些数据收到了哪些数据没收到知道了这些信息就可以只重传丢失的数据。
的头部信息接收到并缓存下来的接收缓冲区查看丢失的数据然后只重发丢失数据即可。
相同会触发快速重传如果之后收到了延时的包此时就会再回一个这一时刻收到的那个重复的包SACK通知发送方这个是网络延时问题。
引入了窗口这个概念。
即使在往返时间较长的情况下它也不会降低网络通信的效率。
那么有了窗口就可以指定窗口大小窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。
来确认应答判断是否收到数据。
这个模式就叫累计确认或者累计应答。
Window也就是窗口大小。
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。
于是发送端就可以根据这个接收端的处理能力来发送数据而不会导致接收端处理不过来。
所以通常窗口大小是由接收方决定的。
只有在窗口还有剩余的情况下发送方才可以继续发送如果收到了应答那么就会滑动窗口也就是又有可以发送的数据了这时就会继续发送直到窗口再度耗尽。
滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。
其中两个指针是绝对指针指特定的序列号一个是相对指针需要做偏移。
同时注意接收窗口的大小是约等于发送窗口的大小的。
滑动窗口并不是一成不变的。
提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量这就是所谓的流量控制。
实际上发送窗口和接收窗口中所存放的字节数都是放在操作系统内存缓冲区中的而操作系统的缓冲区会被操作系统调整。
这种调整可以看下面两个图的例子来感受一下就不配说明性的文字了图已经比较清晰的展示出来了
时发送方实际上会定时发送窗口探测报文以便知道接收方的窗口是否发生了改变这个内容后面会进一步学习。
上图的情况就会出现最后发送端的窗口出现负值。
为了规避这种情况TCP
规定是不允许同时减少缓存又收缩窗口的而是采用先收缩窗口过段时间再减少缓存这样就可以避免了丢包情况。
如果接收方来不及取走窗口数据那么就会导致窗口的减小如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口而发送方会义无反顾地发送这几个字节这就是糊涂窗口综合症。
也就是说在接收方来不及取出数据的时候就会告知窗口大小然后每次循环就会导致最后窗口越来越小每次只能发送一点点数据。
那么为了解决这个问题一般就是同时解决两个方面接收方不通告小窗口发送方避免发送小数据。
MSS或者接收方缓存空间超过一半可用再打开窗口让发送方发送数据。
算法该算法的思路是延时处理只有满足下面两个条件中的任意一个条件才可以发送数据
流量控制是避免「发送方」的数据填满「接收方」的缓存但是并不知道网络的中发生了什么。
在网络出现拥堵时如果继续发送大量数据包可能会导致数据包时延、丢失等这时
就会重传数据但是一重传就会导致网络的负担更重于是会导致更大的延迟以及更多的丢包这个情况就会进入恶性循环被不断地放大…
所以为了在「发送方」调节所要发送数据的量定义了一个叫做「拥塞窗口」的概念。
拥塞窗口
是发送方维护的一个的状态变量它会根据网络的拥塞程度动态变化的。
是约等于的关系那么由于加入了拥塞窗口的概念后此时发送窗口的值是swnd
1/cwnd。
也就是说进入拥塞避免算法发包的个数变成了线性增长。
拥塞发生后就会触发重传机制一般而言有两个一个是超时重传一个是快速重传。
两者的拥塞发送算法不同。
ACK于是发送端就会快速地重传不必等待超时再重传。
这时相当于只丢了一小部分的包所以并不严重。
时的数据都已收到该恢复过程已经结束可以回到恢复之前的状态了也即再次进入拥塞避免状态
打开就可以直观的来分析数据。
点开每一个数据包还可以看到各个协议栈各层的信息如下
延迟确认机制」那么第二和第三次挥手就会合并传输这样就出现了三次挥手。
秒才可以发现一个「死亡」连接于是客户端连接就会断开连接。
如果客户端发送了数据包一直没有收到服务端对该数据包的确认报文则会一直重传该数据包直到重传次数超过
服务器会出现繁忙的情况当应用程序读取速度慢那么缓存空间会慢慢被占满于是为了保证发送方发送的数据不会超过缓冲区大小服务器则会调整窗口大小的值接着通过
报文通知给对方告知现在的接收窗口大小从而控制发送方发送的数据大小。
0发送方会定时发送窗口大小探测报文以便及时知道接收方窗口大小的变化。
超时时间会翻倍递增。
将会延迟一段时间以等待是否有响应数据可以一起发送如果在延迟等待发送
后内核会把连接从半连接队列移除然后创建新的完全的连接并将其添加到
Recv-Q当前全连接队列的大小也就是当前已完成三次握手并等待服务端
Recv-Q已收到但未被应用进程读取的字节数Send-Q已发送但未收到确认的字节数
全连接队溢出的时候后续的请求就会被丢弃这样就会出现服务端请求数量上不去的现象。
当然也可以修改变成向客户端发送
ACK请求就会被多次重发。
如果服务器上的进程只是短暂的繁忙造成
ACK仍然会触发服务器端成功建立连接。
所以tcp_abort_on_overflow
tcp_syncookies则会丢弃若全连接队列满了且没有重传
2)如果「当前半连接队列」超过「理论半连接队列最大值」那么处于
半连接队列的情况下成功建立连接在前面我们源码分析也可以看到这点当开启了
包表明确认收到了客户端的序列号同时也把自己的序列号发给对方并将自己的状态切换至
SYN_RECV这时候内核就会构建「半连接队列」来维护「未完成」的握手信息当半连接队列溢出后服务端就无法再建立新的连接。
连接因为半连接队列溢出而被丢弃。
隔几秒执行几次如果有上升的趋势说明当前存在半连接队列溢出的现象。
第一次握手就是正常的三次握手流程。
但如果之后再次建立连接这时客户端发送
和数据确认并将数据进一步给应用进程处理无效则会丢弃只发送正常的
FIN_WAIT2也就是表示主动方的发送通道就关闭了。
当被动方进入
报文就直接关闭连接了不需要走四次挥手流程是一个暴力关闭连接的方式。
函数意味着完全断开连接完全断开不仅指无法传输数据而且也不能发送数据。
函数关闭连接此时连接就会是「孤儿连接」因为它无法再发送和接收数据。
如果孤儿连接数量大于它新增的孤儿连接将不再走四次挥手而是直接发送
函数关闭的孤儿连接由于无法再发送和接收数据所以这个状态不可以持续太久而
是主动方四次挥手的最后一个状态也是最常遇见的状态。
当收到被动方发来的
状态存在的必要之前已经学过了可以往上翻到最前面刚学四次挥手的地方。
**主要是两点
防止历史连接中数据被后面的四元组连接错误接受保证「被动关闭连接」的一方能被正确的关闭
参数减少不同连接间数据错乱的概率。
tcp_max_tw_buckets
参数。
但是需要注意该参数是只用于客户端建立连接的发起方因为是在调用
时起作用的而对于服务端被动连接方是没有用的。
同时还有一个前提是双方都需要打开时间戳tcp_timestamps
就会因为重复数据包的时间戳过期直接舍弃还可以防止序列号的绕回。
是全双工连接就有可能出现客户端和服务端同时关闭连接两者都认为自己是主动方发送
连接是由内核维护的内核会为每个连接建立内存缓冲区内存过小就会无法充分利用网络带宽内存过大会导致服务器资源耗尽无法建立新连接。
会保证每一个报文都能够抵达对方它的机制是这样报文发出去后必须接收到对方返回的确认报文
提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量这就是滑动窗口的由来。
同时要注意发送方的窗口和接收方的窗口都是动态变化的如果不考虑拥塞控制那么两者是约等于的关系。
但是因为网络的传输能力是有限的当发送方依据发送窗口发送超过网络处理能力的报文时路由器会直接丢弃这些报文。
因此缓冲区的内存并不是越大越好。
网络是有「带宽」限制的带宽描述的是网络传输能力它与内核缓冲区的计量单位不同:
带宽时延积它决定网络中飞行报文的大小由于发送缓冲区大小决定了发送窗口的上限而发送窗口又决定了「已发送未确认」的飞行报文的上限。
因此发送缓冲区不能超过「带宽时延积」。
第一个数值是动态范围的最小值表示即使在内存压力下也可以保证的最小接收缓冲区大小4096
接收缓冲区可以根据系统空闲内存的大小来调节接收窗口但是不是自动开启的需要配置
同时引入了新的问题接收缓冲区调节时怎么知道当前内存是否紧张或充分呢这是通过
在高并发服务器中为了兼顾网速与大量的并发连接我们应当保证缓冲区的动态调整的最大值达到带宽时延积而最小值保持默认的
不变即可。
而对于内存紧张的服务而言调低默认值是提高并发的有效手段。
连接使用更多的系统内存这有利于提升并发能力。
需要注意的是tcp_wmem
所以接收方一定要知道发送方的消息长度不然无法解析成一个完成的用户消息。
函数完成数据“发送”以后数据并没有被真正从网络上发送出去只是从应用程序拷贝到了操作系统内核协议栈中。
至于什么时候真正被发送取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。
最简单的方法直接规定每一个消息的长度是相通的。
但是使用很不灵活很少使用。
可以在两个用户消息之间插入特殊的字符接收数据时读到这个字符相当于读完了一个完整消息。
可以自定义一个消息结构由包头和数据组成其中包头包是固定大小的而且包头里有一个字段来说明紧随其后的数据有多大。
时长后历史报文会自动消失但前提是能正常四次挥手。
例如如下的情况就能很好的解释为什么可能有历史报文
当然即使客户端和服务端的初始化序列号不一样也会存在收到历史报文的可能。
但是历史报文是否接收还要看序列号是否在接收窗口只有在窗口才会接收。
如果每次连接客户端和服务端序列号均不相同那么大概率历史报文序列号「不在」对方接收窗口从而很大程度上避免了历史报文。
为每个传输方向上的每个字节都赋予了一个编号以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。
序列号是一个
建立连接的时候客户端和服务端都会各自生成一个初始序列号它是基于时钟生成的一个随机数来保证每个连接都拥有不同的初始序列号。
初始化序列号可被视为一个
也就是说序列号和初始化序列号均会发生回绕无法根据序列号来直接判断新老数据。
为了解决这个问题就需要有
另一个是能防止序列号回绕PAWS。
如果发现收到的数据包中时间戳不是递增的则表示该数据包是过期的就会直接丢弃这个数据包。
函数会放过这个特殊的情况认为是合法的可以接收该数据包。
所以解决这个问题可以如下出发
bit。
会造成兼容问题。
将与时钟频率无关的值作为时间戳时钟频率可以增加但时间戳增速不变。
这样报文太多一样会导致时间戳失去意义。
防止具有相同四元组的旧数据包被收到也就是防止历史连接中的数据被后面的连接接受否则就会导致后面的连接收到一个无效的数据保证「被动关闭连接」的一方能被正确的关闭即保证最后的
net.ipv4.tcp_tw_reuse如果开启该选项的话客户端连接发起方
函数时如果内核选择到的端口已经被相同四元组的连接占用的时候就会判断该连接是否处于
秒那么就会重用这个连接然后就可以正常使用该端口了。
所以该选项只适用于连接发起方。
net.ipv4.tcp_tw_recycle如果开启该选项的话允许处于
后内核会把连接从半连接队列移除然后创建新的完全的连接并将其添加到
这时后续的连接就会被丢弃这样就会出现服务端请求数量上不去的现象。
报文时取出该值验证如果合法就认为连接建立成功。
如果要开启只需要设置
报文的初始化序列号其实是一个随机数会回复一个携带了正确序列号和确认号的
报文来完成关闭要注意的是必须同时满足「四元组相同」和「序列号是对方期望的」这两个条件。
ACK他的报文确认号就是服务端下一次想要接收的序列号。
用这个序列号作为
等再次收到前面被网络延迟的数据包时会判断乱序队列有没有数据然后会检测乱序队列中是否有可用的数据如果能在乱序队列中找到与当前报文的序列号保持的顺序的报文就会看该报文是否有
的「序列号和时间戳」是否合法然后根据判断结果的不同做不同的处理。
变量保存。
然后会计算下一次期望收到的序列号本次例子下一次期望收到的序列号就是
防止历史连接中的数据被后面相同四元组的连接错误的接收保证「被动关闭连接」的一方能被正确的关闭
keepalive也就是关闭了保活机制不会发送探测报文。
这个机制如下
保活时间的到来。
如果对端主机崩溃或对端由于其他原因导致报文不可达。
当
保活的探测报文发送给对端后石沉大海没有响应连续几次达到保活探测次数后TCP
报文后续的挥手过程也都是在内核完成并不需要进程的参与所以即使服务端的进程退出了还是能与客户端完成
在客户端主机宕机后服务端向客户端发送的报文会得不到任何的响应在一定时长后服务端就会触发超时重传机制重传未得到响应的报文。
服务端传输给客户端无响应那么一段时间后就会触发服务端的超时重传。
如果在服务端重传报文的过程中客户端刚好把网线插回去了由于拔掉网线并不会改变客户端的
状态所以这时客户端是可以正常接收服务端发来的数据报文的然后客户端就会回
如果如果在服务端重传报文的过程中客户端一直没有将网线插回去服务端超时重传报文的次数达到一定阈值后内核就会判定出该
连接就会断开。
而等客户端插回网线后如果客户端向服务端发送了数据由于服务端已经没有与客户端相同四元祖的
机制在客户端拔掉网线后并且双方都没有进行数据传输那么客户端和服务端的
机制在客户端拔掉网线后即使双方都没有进行数据传输在持续一段时间后TCP
保活时间的到来。
如果对端主机宕机注意不是进程崩溃进程崩溃后操作系统在回收进程资源的时候会发送
保活机制来探测对方是不是发生了主机宕机或对端由于其他原因导致报文不可达。
当
保活的探测报文发送给对端后石沉大海没有响应连续几次达到保活探测次数后TCP
防止历史连接中的数据被后面相同四元组的连接错误的接收保证「被动关闭连接」的一方能被正确的关闭
PORT」都一样的服务器发起连接了但是被使用的端口还是可以继续对另外一个服务器发起连接的。
不过即使是在这种场景下只要连接的是不同的服务器端口是可以重复使用的所以客户端还是可以向其他服务器发起连接的这是因为内核在定位一个连接的时候是通过四元组源IP、源端口、目的IP、目的端口信息来定位的并不会因为客户端的端口一样而导致连接冲突。
net.ipv4.tcp_tw_reuse如果开启该选项的话客户端连接发起方
函数时如果内核选择到的端口已经被相同四元组的连接占用的时候就会判断该连接是否处于
秒那么就会重用这个连接然后就可以正常使用该端口了。
所以该选项只适用于连接发起方。
net.ipv4.tcp_tw_recycle如果开启该选项的话允许处于
三次握手后才能进行。
当然也有情况是可以同时握手的需要以下两个条件同时满足
连接建立的时延。
要使用该功能客户端和服务端需要同时支持该功能。
后续通信客户端第一次握手就可以携带数据从而绕过三次握手发送数据
和「数据」进行确认服务器随后将「应用数据」递送给对应的应用程序如果
报文中的「应用数据」服务器可在握手完成之前发送「响应数据」这就减少了握手带来的
报文中发送的「应用数据」没有被确认则客户端将重新发送「应用数据」此后的
0-RTT用“pre_shared_key”和“early_data”扩展在
请求服务端收到后就返回响应至此「请求-应答」的模式就完成了随后就会释放
如果每一次都重复如上过程那就是短连接。
如果第一次请求完先不断开
Keep-Alive它必须在请求的包头中添加对应字段然后服务器收到请求同样也要添加这个响应字段。
从
流水线是客户端可以先一次性发送多个请求而在发送过程中不需先等待服务器的回应可以减少整体的响应时间。
当然服务器还是按照顺序响应。
长连接的超时时间。
这就相当于给定时器计时超时就会触发回调函数释放该连接。
这个特性是不错但是它需要服务端和客户端的操作系统同时支持才能体验到而
年提出的所以市面上依然有很多老式的操作系统不支持而升级操作系统是很麻烦的事情因此
层为了保证数据的有序性只有在处理完有序的数据后滑动窗口才能往前滑动否则就停留。
滑动窗口那么当发生数据丢失滑动窗口是无法往前移动的此时就会阻塞住所有的
帧告诉对端自己可以接收的字节数这样发送方就不会发送超过这个数量的数据。
通过
消耗连接Connection的全部接收缓冲。
Connection
拥塞控制算法我们熟知的慢开始、拥塞避免、快重传、快恢复策略同时也支持
CubicBytes、Reno、RenoBytes、BBR、PCC
是处于应用层的应用程序层面就能实现不同的拥塞控制算法不需要操作系统不需要内核支持。
这是一个飞跃因为传统的
拥塞控制必须要端到端的网络协议栈支持才能实现控制效果。
而内核和操作系统的部署成本非常高升级周期很长所以
的拥塞控制算法就可以有较快的迭代速度。
可以针对不同应用场景设置不同的拥塞控制算法。
就可以「同时」完成建立连接与密钥协商甚至在第二次连接的时候应用数据包可以和
密钥等就可以“无缝”地复用原连接消除重连的成本没有丝毫卡顿感达到了连接迁移的功能。
地址来寻找网络中互连的主机或路由器。
在传输层中需要通过端口进行寻址来识别同一计算机中同时通信的不同应用程序。
所以传输层的「端口号」的作用是为了区分同一个主机上不同应用程序的数据包。
传输层有两个传输协议分别是
TCP/UDP所以可以根据这个信息确定送给哪个模块TCP/UDP处理送给
模块的报文根据「端口号」确定送给哪个应用程序处理。
所以这两者共用端口并不会造成冲突过程如下
进程也会有这个报错这是因为重启的时候相当于发生了四次挥手那么主动关闭方就会处于
连接是由四元组源IP地址源端口目的IP地址目的端口唯一确认的那么只要四元组中其中一个元素发生了变化那么就表示不同的
函数时如果选择到的端口已经被相同四元组的连接占用的时候就会判断该连接是否处于
可以的客户端是可以自己连自己的形成连接TCP自连接也可以两个客户端同时向对方发出请求建立连接TCP同时打开这两个情况都有个共同点就是没有服务端参与也就是没有listen就能建立连接。
bind绑定服务器端的IP和端口所有客户端都向这个IP和端口发送和请求数据*/
服务器等待客户端的链接返回值cfd为客户端的socket描述符*/
实验抓包得到结果**就算不执行accept()方法三次握手照常进行并顺利建立连接。
**而且在服务端执行accept()前如果客户端发送消息给服务端服务端是能够正常回复ack确认包的。
这里就回到了之前的半连接队列和全连接队列全连接队列就是第三次握手之后会从半连接队列中取出
半连接队列却不太一样因为队列里的都是不完整的连接嗷嗷等待着第三次握手的到来。
那么现在有一个第三次握手来了则需要从队列里把相应IP端口的连接取出如果半连接队列还是个链表那我们就需要依次遍历才能拿到我们想要的那个连接算法复杂度就是O(n)。
如果队列满了服务端还收到客户端的第三次握手ACK默认当然会丢弃这个ACK。
会丢弃这个第三次握手ACK包并且开启定时器重传第二次握手的SYNACK如果重传超过一定限制次数还会把对应的半连接队列里的连接给删掉。
全连接队列满了之后就直接发RST给客户端效果上看就是连接断了。
发来服务端不会将其放入半连接队列中而是直接生成一个cookies这个cookies会跟着第二次握手发回客户端。
客户端在发第三次握手的时候带上这个cookies服务端验证到它就是当初发出去的那个就会建立连接并放入到全连接队列中。
可以看出整个过程不再需要半连接队列的参与。
注意cookies并不会有一个专门的队列保存它是通过通信双方的IP地址端口、时间戳、MSS等信息进行实时计算的保存在TCP报头的seq里。
服务端并不会保存连接信息所以如果传输过程中数据包丢了也不会重发第二次握手的信息。
的利用这一点如果此时攻击者构造大量的第三次握手包ACK包同时带上各种瞎编的cookies信息就会消耗很多资源。
一个数据包从聊天框里发出消息会从聊天软件所在的用户空间拷贝到内核空间的发送缓冲区send
buffer数据包就这样顺着传输层、网络层进入到数据链路层在这里数据包会经过流控qdisc再通过RingBuffer发到物理层的网卡。
数据就这样顺着网卡发到了纷繁复杂的网络世界里。
这里头数据会经过n多个路由器和交换机之间的跳转最后到达目的机器的网卡处。
此时目的机器的网卡会通知DMA将数据包信息放到RingBuffer中再触发一个硬中断给CPUCPU触发软中断让ksoftirqd去RingBuffer收包于是一个数据包就这样顺着物理层数据链路层网络层传输层最后从内核空间拷贝到用户空间里的聊天软件里。
让数据按一定的规则排个队依次处理也就是所谓的qdisc(Queueing
Disciplines排队规则)这也是我们常说的流量控制机制。
可以通过下面的ifconfig命令查看到里面涉及到的txqueuelen后面的数字1000其实就是流控队列的长度。
当发送数据过快流控队列长度txqueuelen又不够大时就容易出现丢包现象。
接收数据时会将数据暂存到RingBuffer接收缓冲区中然后等着内核触发软中断慢慢收走。
如果这个缓冲区过小而这时候发送的数据又过快就有可能发生溢出此时也会产生丢包。
一个网卡里是可以有多个RingBuffer的所以上面的rx_queue_0_drops里的0代表的是第0个RingBuffer的丢包数对于多队列的网卡这个0还可以改成其他数字。
网卡作为硬件传输速度是有上限的。
当网络传输速度过大达到网卡上限时就会发生丢包。
socket进行网络编程的时候内核都会分配一个发送缓冲区和一个接收缓冲区。
发送时将数据拷贝到内核发送缓冲区就完事返回了至于什么时候发数据发多少数据这个后续由内核自己做决定。
接收缓冲区作用也类似从外部网络收到的数据包就暂存在这个地方然后坐等用户空间的应用程序将数据包取走。
4194304不管是接收缓冲区还是发送缓冲区都能看到三个数值分别对应缓冲区的最小值默认值和最大值
min、default、max。
缓冲区会在min和max之间动态调整。
对于发送缓冲区执行send的时候如果是阻塞调用那就会等等到缓冲区有空位可以发数据。
如果是非阻塞调用就会立刻返回一个
again。
让应用程序下次再重试。
这种情况下一般不会发生丢包。
当接受缓冲区满了事情就不一样了它的TCP接收窗口会变为0也就是所谓的零窗口并且会通过数据包里的win0告诉发送端不要发消息了。
一般这种情况下发送端就该停止发消息了但如果这时候确实还有数据发来就会发生丢包。
需要知道目的地的域名。
想知道你的机器到服务器之间有没有产生丢包行为。
可以使用ping命令。
mtr命令可以查看到你的机器和目的机器之间的每个节点的丢包情况。
可以看到Host那一列出现的都是链路中间每一跳的机器Loss的那一列就是指这一跳对应的丢包率。
同时因为mtr默认用的是ICMP包有些节点限制了ICMP包导致不能正常展示。
可以加一个参数使用
把ICMP包和UDP包的结果拼在一起看就是比较完整的链路图了。
有个小细节Loss那一列我们在icmp的场景下关注最后一行如果是0%那不管前面loss是100%还是80%都无所谓那些都是节点限制导致的虚报。
建立了TCP连接的两端发送端在发出数据后会等待接收端回复ack包ack包的目的是为了告诉对方自己确实收到了数据但如果中间链路发生了丢包那发送端会迟迟收不到确认ack于是就会进行重传。
以此来保证每个数据包都确确实实到达了接收端。
TCP位于传输层在它的上面还有各种应用层协议比如常见的HTTP或者各类RPC协议。
TCP保证的可靠性是传输层的可靠性。
也就是说TCP只保证数据从A机器的传输层可靠地发到B机器的传输层。
这时就有可能在完成了传输层的正常传输之后应用层需要从接收缓冲区取出数据这时就可能导致丢包
服务器可能记录了我们最近发过什么数据假设每条消息都有个id服务器和聊天软件每次都拿最新消息的id进行对比就能知道两端消息是否一致就像对账一样。
对于发送方只要定时跟服务端的内容对账一下就知道哪条消息没发送成功直接重发就好了。
如果接收方的聊天软件崩溃了重启后跟服务器稍微通信一下就知道少了哪条数据同步上来就是了所以也不存在上面提到的丢包情况。
可以看出TCP只保证传输层的消息可靠性并不保证应用层的消息可靠性。
如果我们还想保证应用层的消息可靠性就需要应用层自己去实现逻辑做保证。
第一如果是两端通信你聊天软件里有1000个好友你就得建立1000个连接。
但如果引入服务端你只需要跟服务器建立1个连接就够了聊天软件消耗的资源越少手机就越省电。
第二就是安全问题如果还是两端通信随便一个人找你对账一下你就把聊天记录给同步过去了这并不合适吧。
如果对方别有用心信息就泄露了。
引入第三方服务端就可以很方便的做各种鉴权校验。
第三是软件版本问题。
软件装到用户手机之后软件更不更新就是由用户说了算了。
如果还是两端通信且两端的软件版本跨度太大很容易产生各种兼容性问题但引入第三端服务器就可以强制部分过低版本升级否则不能使用软件。
但对于大部分兼容性问题给服务端加兼容逻辑就好了不需要强制用户更新软件。
0这时服务端应用程序如果有数据要发送的话就发完数据后才调用关闭连接的函数如果服务端应用程序没有数据要发送的话可以直接调用关闭连接的函数这时服务端就会发一个
应答报文但是服务端应用程序可能还有数据要发送所以并不能马上发送
如果服务端应用程序有数据要发送的话就发完数据后才调用关闭连接的函数如果服务端应用程序没有数据要发送的话可以直接调用关闭连接的函数
不再有发送数据的能力但是还是具有接收数据的能力。
如果有多进程/多线程共享同一个
函数这时如果挥手的时候客户端收到了服务端数据因为客户端无法收发数据就会直接回
报文然后内核释放连接。
此时就不会经历完整四次挥手是一种粗暴的关闭。
当服务端收到
后内核就会释放连接当服务端应用程序再次发起读操作或者写操作时就能感知到连接已经被释放了
信号应用层代码可以捕获并处理信号如果不处理则默认情况下进程会终止异常退出。
四次挥手过程中如果收到了服务端发送的数据客户端也是可以正常读取到该数据的然后就会经历完整的
延迟确认机制」那么第二和第三次挥手就会合并传输这样就出现了三次挥手。
延迟确认机制是默认开启的所以导致我们抓包时看见三次挥手的次数比四次挥手还多。
TCP
将会延迟一段时间以等待是否有响应数据可以一起发送如果在延迟等待发送
报文传给接收端主机每发送一次数据就「累加」一次该「数据字节数」的大小。
用来解决网络包乱序问题。
确认号指下一次「期望」收到的数据的序列号发送端收到接收方发来的
确认报文以后就可以认为在这个序号以前的数据都已经被正常接收。
用来解决丢包的问题。
控制位用来标识
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback