1. Tcp和udp的区别? Ø TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,UDP主要用于那些对高速传输和实时性有较高要求的通信或广播通信。 两者的区别大致如下: • TCP面向连接,UDP面向非连接即发送数据前不需要建立链接; • TCP提供可靠的数据传输(拥塞控制、丢包重发、顺序控制),UDP无法保证可靠交付; • TCP面向字节流,UDP面向数据报文段; • 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信 • TCP首部开销20字节;UDP的首部开销小,只有8个字节 • TCP数据传输慢,UDP数据传输快; UDP的应用场景:qq语音,qq视频,直播等 TCP的应用场景:文件传输、发送,接收邮件,远程登录等 2. Tcp的三次握手和四次挥手?为什么要三次握手?为什么连接需要3次,而断开需要4次?为什么有TIME_WAIT状态且TIME_WAIT等待的时间是2MSL?为什么会有CLOSE_WAIT状态?出现大量的TIME_WAIT是什么原因?要怎么解决呢? Ø 三次握手 第一次握手:建立连接,客户端发送连接请求报文段,将SYN位 置为1,sequence Number为x:然后,客户端进入SYN_SENT状态,等待服务器确认. 第二次握手:服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置AcknowledgmentNumber 为x+1(Sequence Number+1);同时,自己还要发送SYN请求信息,将SYN位置为1, Sequence Number为y:服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RCVD状态。 第三次握手:客户端接收到SYN_ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕之后,客户 端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。 Ø 四次挥手 第一次挥手(FIN=1,seq=x) 主机1(可以是客户端,也可以是服务器端),设置seq=x,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了; 第二次挥手(ACK=1,ACKnum=x+1) 主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknum=x+1,主机1进入FIN_WAIT_2状态,主机2处于CLOSE_WAIT状态;主机2告诉主机1,我“同意”你的关闭请求; 第三次挥手(FIN=1,seq=y) 主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK 状态 第四次挥手(ACK=1,ACKnum=y+1) 主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了,进入 CLOSED 状态。 主机 1 等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。 Ø 为什么要三次握手 三次握手的目的是为了建立可靠的通信信道,让双方确认自己与对方的发送和接收都是正常的。简单来说就是,我需要连接上对方,并且让对方知道我已经连接上对方了。如果是两次握手,无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号; • 防止旧的重复连接初始化造成混乱(主要原因) 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接。 具体例子:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。” • 同步双方的初始序列号 那当服务端发送初始序列号给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。 • 避免资源浪费 由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,所以每收到一个 SYN就只能先主动建立一个连接。如果客户端的SYN阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。 https://www.jianshu.com/p/cf1bed23f7fb Ø 为什么连接需要3次,而断开需要4次 • 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。 • 服务器端收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才会发送FIN报文给客户端来表示同意现在关闭连接。由于 TCP 协议是全双工的,也就是说客户端和服务端都可以发起断开连接。两边各发起一次断开连接的申请,加上各自的两次确认,看起来就像执行了四次挥手。 • 从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACK和FIN一般都会分开发送,而当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文(其中ACK报文是用来应答的,SYN报文是用来同步的),从而比三次握手导致多了一次。 Ø 为什么有TIME_WAIT状态? 按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假想网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来防止最后一次ACK丢失,重发可能丢失的ACK报文。 Ø 为什么TIME_WAIT等待的时间是2MSL? MSL,Maximum Segment Lifetime英文的缩写,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间将被丢弃。Time_wait 是从客户端接收到 FIN 后发送 ACK 开始计时的,如果在 TIME-WAIT 时间内,客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那 2MSL 将重新计时。 2MSL是为了保证被动关闭方能够收到主动关闭方发的最后一次ACK报文。如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另一方接收到 FIN 后,会重发ACK给被动关闭方,一来一去正好 2 个 MSL(被动关闭方收到最后一次报文的最大时间 + 如果没有收到的话,主动关闭方收到被动关闭方重发FIN报文的最大时间)。 所以TIME_WAIT的等待时间是2MSL主要有两个目的: • 可靠地实现TCP全双工连接的终止。(确保最后的ACK能让被关闭方接收) • 让老的重复数据包在网络中消逝。(TCP中是可靠的服务,当数据包丢失会重传,当有数据包迷路的情况下,如果不等待2MSL时,当客户端以同样地方式重新和服务建立连接后,上一次迷路的数据包这时可能会到达服务,这时会造成旧包被重新读取。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。) Ø 为什么会有CLOSE_WAIT状态? CLOSE_WAIT状态是介于收到主动关闭方FIN报文和被动关闭方发送FIN报文之间。正常情况下,是被动关闭方还有数据要处理和发送;如果有大量一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号,关闭sokcet不及时。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。可能是程序Bug,接收到FIN信号后没有及时关闭socket,这可能是Netty的Bug,也可能是业务层Bug。例如I/O线程被意外阻塞,或者I/O线程执行的用户自定义Task比例过高,导致I/O操作处理不及时,链路不能被及时释放。 当有大量CLOSE_WAIT状态的时候,需要排查服务端是否有业务异常。之前我们遇到过访问接口一直报502的情况,查看发现有大量CLOSE_WAIT的链接,且Recv_Q值不为0,一般是DOS攻击引起的(查看有突发大流量进行压测,耗尽了系统资源(缓冲区队列满了,Recv_Q的值有的是5万多,服务器不在接受新的请求),服务被压垮了)。 Ø 出现大量的TIME_WAIT是什么原因?要怎么解决呢? • TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击;在高并发短连接的 TCP 服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量 socket 处于 TIME_WAIT 状态。如果客户端的并发量持续很高,因为端口有限,内存有限,会导致此时部分客户端显示连接不上。在Linux内核中 TIME_WAIT = 60秒。 • 解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。可通过调整内核参数解决: 编辑文件/etc/sysctl.conf,加入以下内容: net.ipv4.tcp_syncookies = 1; 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1; 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1;表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout = 30;修改系默认的 TIMEOUT 时间 然后执行 /sbin/sysctl -p 让参数生效。 Ø CLOSE_WAIT和TIME_WAIT的区别,可以通过一个列子描述一下: 服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。 所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。 3. Tcp是怎么保证消息的可靠传输的?网络的拥塞控制和流量控制分别是指什么?拥塞控制和流量控制的区别? Ø 对于可靠性,TCP通过以下方式进行保证: · 数据包校验:在数据传输的过程中,将发送的数据段都当做一个16位的整数。将这些整数加起来。并且前面的进位不能丢弃,补在后面,最后取反,得到校验和。 目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时TCP发送数据端超时后会重发数据; · 确认应答+序列号: TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。 对失序数据包重排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序,然后才交给应用层; 丢弃重复数据:对于重复数据,能够丢弃重复数据; 应答机制:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。这个确认不是立即发送,通常将推迟几分之一秒; · 超时重发:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段; 发送方没有接收到响应的ACK报文原因可能有两点: a、数据在传输过程中由于网络原因等直接全体丢包,接收方没有接收到。 b、接收方接收到了响应的数据,但是发送的ACK报文响应却由于网络原因丢包了。 如果是刚才第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号,所以上面说序列号还有去除重复数据的作用),那么直接丢弃,仍旧发送ACK应答。 注意:超时以500ms(0.5秒)为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待2*500ms的时间后,再次重传。等待4*500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。 · 流量控制:TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。 · 拥塞控制:当网络拥塞时,减少数据的发送。 Ø 流量控制: 接收端在接收到数据后,对其进行处理。我们的接收方有一块接收缓存,当数据来到时会先把数据放到缓存中,上层应用等缓存中有数据时就会到缓存中取数据。如果发送端的发送速度太快,导致接收端的接收缓冲区很快的填充满了。假如发送方没有限制地不断地向接收方发送数据,接收方的应用程序又没有及时把接收缓存中的数据读走,就会出现缓存溢出,数据丢失的现象。而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。  在TCP协议的报头信息当中,有一个16位字段的窗口大小。在介绍这个窗口大小时我们知道,窗口大小的内容实际上是接收端接收数据缓冲区的剩余大小。这个数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。 利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。 从上面的图可以看到滑动窗口左边的是已发送并且被确认的分组,滑动窗口右边是还没有轮到的分组。滑动窗口里面也分为两块,一块是已经发送但是未被确认的分组,另一块是窗口内等待发送的分组。随着已发送的分组不断被确认,窗口内等待发送的分组也会不断被发送。整个窗口就会往右移动,让还没轮到的分组进入窗口内。 可以看到滑动窗口起到了一个限流的作用,也就是说当前滑动窗口的大小决定了当前 TCP 发送包的速率,而滑动窗口的大小取决于拥塞控制窗口和流量控制窗口的两者间的最小值。 TCP是以一个段为单位进行数据的传输的,每发送一个段,就会等待对端主机的针对这个段的确认应答信号ACK,但这样的传输方式的缺点也很明显,就是:当数据包的往返时间越长,通信性能越低。利用滑动窗口控制可以提高速度。确认应答包不再以每个段为单位进行确认了,而是以更大的单位进行确认,转发时间将会被大幅度的缩短。也就是说,发送端主机在发送了一个段之后,没必要一直等待对端主机的确认应答信号,而是继续发送。窗口大小,指的就是无需等待接收端主机的确认应答信号而可以持续发送的数据的最大值,或者说段的最大值。滑动窗口控制的实现,使用了大量的缓冲区,通过对多个段的数据同时进行确认应答来实现高效传输。 Ø 拥塞控制: 在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大。当网络发送拥塞时,TCP会自我牺牲,降低发送的数据量。于是,就有了拥塞控制,控制的目的就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。 拥塞窗口(congestion window) 为了进行拥塞控制,TCP 发送方要维持⼀个 **拥塞窗口(cwnd) **的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的⼀个。 拥塞窗口cwnd变化的规则: 只要网络中没有出现拥塞, cwnd 就会增大; 但网络中出现了拥塞, cwnd 就减少; 拥塞控制的方法主要有以下四种: · 慢启动: 不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小;cwnd初始值为1,当发送方每收到一个 ACK,cwnd加倍; · 拥塞避免: 慢启动算法,发包的个数是指数性的增长,当拥塞窗口 >= 阀值(慢启动门限 ssthresh)时,就会进入“拥塞避免算法”。这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。是一个线性上升的算法。          · 拥塞发生: 当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:超时重传和快速重传。当发生了超时重传,就会使用拥塞发生算法。这个时候,慢启动门限ssthresh 和 cwnd 的值会发生变化:ssthresh ==cwnd/2,cwnd=1。接着,就重新开始慢启动,慢启动是会突然减少数据流的。这真是一旦超时重传,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。 快重传要求接收方在收到一个 失序的报文段 后就立即发出 重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。          · 快速恢复: 快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像超时重传那么强烈。在进入快速恢复之前, cwnd 和 ssthresh 已被更新了:cwnd=cwnd/2,ssthresh=cwnd。然后,进入快速恢复算法如下: 拥塞窗口cwnd=ssthresh+3(3 的意思是确认有 3 个数据包被收到了); 重传丢失的数据包; 如果再收到重复的 ACK,那么 cwnd 增加 1; 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态。 https://www.jianshu.com/p/cf1bed23f7fb 拥塞窗口的增长是指数级别的。慢启动的机制只是说明在开始的时候发送的少,发送的慢,但是增长的速度是非常快的。为了控制拥塞窗口的增长,不能使拥塞窗口单纯的加倍,设置一个拥塞窗口的阈值,当拥塞窗口大小超过阈值时,不能再按照指数来增长,而是线性的增长。在慢启动开始的时候,慢启动的阈值等于窗口的最大值,一旦造成网络拥塞,发生超时重传时,慢启动的阈值会为原来的一半(这里的原来指的是发生网络拥塞时拥塞窗口的大小),同时拥塞窗口重置为 1。 https://blog.csdn.net/cbjcry/article/details/84925028 注:第一幅图是快重传+快速恢复场景,第二幅图是超时重传场景 Ø 拥塞控制是⼀个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送方发送数据的速率,以便使接收方来得及接收。 4. Tcp的报文头有了解过,报文头怎么保证消息可靠性? TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能体现在它首部中的各字段的作用。 TCP报文段首部的前20个字节是固定的(下图),后面有4n字节是根据需要而增加的选项(n是整数)。因此TCP首部的最小长度是20字节。 TCP报文首部 • 源端口和目的端口,各占2个字节,分别写入源端口和目的端口; • 序列号(Sequence number),占4字节。序号范围是【0,2^32 - 1】,共2^32个序号。序号增加到 2^32-1后,下一个序号就又回到 0。TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号是301,而接待的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的序号也叫“报文段序号”; • 确认号(Acknowledge number),占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701; • 数据偏移,占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。 • 保留,占6位,保留为今后使用,但目前应置为0; • 紧急URG(URGent),当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据; • 确认ACK(ACKnowledgment),仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1; • 推送PSH(PuSH) ,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1; • 复位RST(ReSeT),当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接; • 同步SYN(SYNchronization),在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1; • 终止FIN(FINis),用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放; • 窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受; • 检验和,占2字节,校验首部和数据这两部分; • 紧急指针,占2字节,指出本报文段中的紧急数据的字节数; • 选项,长度可变,定义一些其他的可选的参数 TCP是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如ip地址、端口号等。 Ø 校验和:查看数据在传输过程中是否发生变化 序列号和确认号:保证接收方可以对失序数据包进行重排序;丢弃重复的数据包;丢包重发 窗口:流量控制 5. TCP为什么引入接收缓存这个数据结构?TCP 粘包/拆包的原因(了解) Ø 如果没有接受缓存的话,或者说只有一个缓存的话,为了保证接受的数据是按顺序传输的,所以如果位于x序号之后的序号分组先到达目的主机的运输层的话必然丢弃,这样的话将在重传上花费很大的开销,所以一般如果有过大的序号达到接收端,那么会按照序号缓存起来等待之前的序号分许到达,然后一并交付到应用进程。 Ø TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象; 6. TCP协议的差错检测 (1)检测哪一部分? TCP协议检测整个用户数据报(首部 + 数据部分) (2)由谁检测? 由目的主机检测 (3)错误之后怎么办? 目的主机发现数据报错误后,直接丢弃。 (4)怎么检测? 不使用复杂的CRC检验,和IP数据报的检测方法相似。 方法:伪首部 + 首部 + 数据部分 + 全0的填充字节 + 16个二进制一组,之后求反码相加。 使用伪首部的原因:只是单纯为了做校验用的。 伪首部包括:伪首部包含源IP地址和目的IP地址,TCP协议号以及TCP长度等信息,目的是让TCP两次检查数据是否已经正确到达目的地。 注意: <1> 伪首部不上传也不向下传输。 <2> 由于检测的部分可能不是偶数个字节,不能均拆成16位的序列,此时可以插入全0的填充字节。 (5)检测过程: <1> 发送方:把待检测的部分分成16位的序列,之后每一个序列进行求反码在求和。并把和的反码放入首部检验和。 <2> 接收方:把待检测的部分分成16位的序列,之后每一个序列进行求反码在求和。之后再与首部检验和相加,为0则正常。不为0则出错。 记忆: (1)IP协议只检测首部,而TCP和UDP检测整个首部和数据部分,当然还包括伪首部。 (2)IP、UDP、TCP检测出错误后,会直接丢弃。但是TCP协议会要求重传。 (3)UDP和TCP的差错检测方法一样。 https://blog.csdn.net/insistgogo/article/details/10296167