TCP协议解析
主要特点:面向连接、面向字节流、全双工通信、通信可靠。优缺点:应用场景:要求通信数据可靠时,即 数据要准确无误地传递给对方。如:传输文件:HTTP、HTTPS、FTP等协议;传输邮件:POP、SMTP等协议ps:首部的前 20 个字节固定,后面有 4n 字节根据需要增加。故 TCP首部最小长度 = 20字节(最大60个字节)。TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。重要字段:客户端与服务器来回共发送三个TCP报文段来建立运输连接,三个TCP报文段分别为:(1)客户端A向服务器B发送的TCP请求报段“SYN=1,seq=x”;(2)服务器B向客户端A发送的TCP确认报文段“SYN=1,ACK=1,seq=y,ack=x+1”;(3)客户端A向服务器B发送的TCP确认报文段“ACK=1,seq=x+1,ack=y+1”。ps:在建立TCP连接之前,客户端和服务器都处于关闭状态(CLOSED),直到客户端主动打开连接,服务器才被动打开连接(处于监听状态 = LISTEN),等待客户端的请求。TCP 协议是一个面向连接的、安全可靠的传输层协议,三次握手的机制是为了保证能建立一个安全可靠的连接。通过上述三次握手,双方确认自己与对方的发送与接收是正常的,就建立起一条TCP连接,即可传送应用层数据。ps:因 TCP提供的是全双工通信,故通信双方的应用进程在任何时候都能发送数据;三次握手期间,任何1次未收到对面的回复,则都会重发。为什么两次握手不行呢?结论:防止服务器接收了早已经失效的连接请求报文,服务器同意连接,从而一直等待客户端请求,最终导致形成死锁、浪费资源。ps:SYN洪泛攻击:(具体见下文)为什么不需要四次握手呢?SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK确认序号标志消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。如何来解决半连接攻击?如何来解决全连接攻击?请注意,现在 TCP 连接还没有释放掉。必须经过时间等待计时器设置的时间 2MSL(MSL:最长报文段寿命)后,客户端才能进入到 CLOSED 状态,然后撤销传输控制块,结束这次 TCP 连接。当然如果服务器一收到 客户端的确认就进入 CLOSED 状态,然后撤销传输控制块。所以在释放连接时,服务器结束 TCP 连接的时间要早于客户端。TCP是全双工的连接,必须两端同时关闭连接,连接才算真正关闭。简言之,客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器才会发送 FIN 连接释放报文,对方确认后就完全关闭了TCP连接。举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。ps:设想这样一个情景:客户端已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用TCP的保活计时器。基本原理:tcp11种状态及变迁其实基本包含在正常的三次握手和四次挥手中,除开CLOSING。正常的三次握手包括4中状态变迁:服务器打开监听(LISTEN)->客户端先发起SYN主动连接标识->服务器回复SYN及ACK确认->客户端再确认即三次握手TCP连接成功。这里边涉及四种状态及变迁:正常的四次握手包含6种tcp状态变迁,如主动发起关闭方为客户端:客户端发送FIN进入FIN_WAIT1 -> 服务器发送ACK确认并进入CLOSE_WAIT(被动关闭)状态->客户端收到ACK确认后进入FIN_WAIT2状态 -> 服务器再发送FIN进入LAST_ACK状态 -> 客户端收到服务器的FIN后发送ACK确认进入TIME_WAIT状态 -> 服务器收到ACK确认后进入CLOSED状态断开连接 -> 客户端在等待2MSL的时间如果期间没有收到服务器的相关包,则进入CLOSED状态断开连接。CLOSING状态:连接断开期间,一般是客户端发送一个FIN,然后服务器回复一个ACK,然后服务器发送完数据后再回复一个FIN,当客户端和服务器同时接受到FIN时,客户端和服务器处于CLOSING状态,也就是此时双方都正在关闭同一个连接。在进入CLOSING状态后,只要收到了对方对自己发送的FIN的ACK,收到FIN的ACK确认就进入TIME_WAIT状态,因此,如果RTT(Round Trip Time TCP包的往返延时)处在一个可接受的范围内,发出的FIN会很快被ACK从而进入到TIME_WAIT状态,CLOSING状态持续的时间就特别短,因此很难看到这种状态。我们知道网络层,可以实现两个主机之间的通信。但是这并不具体,因为,真正进行通信的实体是在主机中的进程,是一个主机中的一个进程与另外一个主机中的一个进程在交换数据。IP协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才应该是应用进程之间的通信。应用场景:UDP协议比TCP协议的效率更高,TCP协议比UDP协议更加安全可靠。下面主要对数据传输出现错误/无应答/堵塞/超时/重复等问题。注意:TCP丢包:TCP是基于不可靠的网路实现可靠传输,肯定会存在丢包问题。如果在通信过程中,发现缺少数据或者丢包,那边么最大的可能性是程序发送过程或者接受过程中出现问题。总结:为了满足TCP协议不丢包,即保证可靠传输,规定如下:注意:TCP丢包有三方面的原因,一是网络的传输质量不好,二是安全策略,三是服务器性能瓶颈先理解2个基础概念:发送窗口、接收窗口工作原理:注意点:关于滑动窗口的知识点:滑动窗口中的数据类型:ARQ解决的问题:出现差错时,让发送方重传差错数据:即 出错重传类型:流量控制和拥塞控制解决的问题:当接收方来不及接收收到的数据时,可通知发送方降低发送数据的效率:即 速度匹配流量控制:注意:拥塞控制:慢开始与拥塞避免:快重传和快恢复:补充:流量控制和拥塞控制的区别什么情况造成TCP粘包和拆包?解决TCP粘包和拆包的方法:传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。https://www.jianshu.com/p/65605622234bhttp://www.open-open.com/lib/view/open1517213611158.htmlhttps://blog.csdn.net/dangzhangjing97/article/details/81008836https://blog.csdn.net/qq_30108237/article/details/107057946https://www.jianshu.com/p/6c73a4585eba

TCP那些事儿
目录:以前我也认为TCP是相当底层的东西,我永远不需要去了解它。虽然差不多是这样,但是实际生活中,你依然可能遇见和TCP算法相关的bug,这时候懂一些TCP的知识就至关重要了。(本文也可以引申为,系统调用,操作系统这些都很重要,这个道理适用于很多东西)这里推荐一篇小短文, 人人都应该懂点TCP使用TCP协议通信的双方必须先建立TCP连接,并在内核中为该连接维持一些必要的数据结构,比如连接的状态、读写缓冲区、定时器等。当通信结束时,双方必须关闭连接以释放这些内核数据。TCP服务基于流,源源不断从一端流向另一端,发送端可以逐字节写入,接收端可以逐字节读出,无需分段。需要注意的几点:TCP状态(11种):eg.以上为TCP三次握手的状态变迁以下为TCP四次挥手的状态变迁服务器通过 listen 系统调用进入LISTEN状态,被动等待客户端连接,也就是所谓的被动打开。一旦监听到SYN(同步报文段)请求,就将该连接放入内核的等待队列,并向客户端发送带SYN的ACK(确认报文段),此时该连接处于SYN_RECVD状态。如果服务器收到客户端返回的ACK,则转到ESTABLISHED状态。这个状态就是连接双方能进行全双工数据传输的状态。而当客户端主动关闭连接时,服务器收到FIN报文,通过返回ACK使连接进入CLOSE_WAIT状态。此状态表示——等待服务器应用程序关闭连接。通常,服务器检测到客户端关闭连接之后,也会立即给客户端发送一个FIN来关闭连接,使连接转移到LAST_ACK状态,等待客户端对最后一个FIN结束报文段的最后一次确认,一旦确认完成,连接就彻底关闭了。客户端通过 connect 系统调用主动与服务器建立连接。此系统调用会首先给服务器发一个SYN,使连接进入SYN_SENT状态。connect 调用可能因为两种原因失败:1. 目标端口不存在(未被任何进程监听)护着该端口被TIME_WAIT状态的连接占用( 详见后文 )。2. 连接超时,在超时时间内未收到服务器的ACK。如果 connect 调用失败,则连接返回初始的CLOSED状态,如果调用成功,则转到ESTABLISHED状态。客户端执行主动关闭时,它会向服务器发送一个FIN,连接进入TIME_WAIT_1状态,如果收到服务器的ACK,进入TIME_WAIT_2状态。此时服务器处于CLOSE_WAIT状态,这一对状态是可能发生办关闭的状态(详见后文)。此时如果服务器发送FIN关闭连接,则客户端会发送ACK进行确认并进入TIME_WAIT状态。流量控制是为了控制发送方发送速率,保证接收方来得及接收。接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。在Linux下有多种实现,比如reno算法,vegas算法和cubic算法等。发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。为了便于讨论,做如下假设:发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。发送端的每个TCP报文都必须得到接收方的应答,才算传输成功。TCP为每个TCP报文段都维护一个重传定时器。发送端在发出一个TCP报文段之后就启动定时器,如果在定时时间类未收到应答,它就将重发该报文段并重置定时器。因为TCP报文段最终在网络层是以IP数据报的形式发送,而IP数据报到达接收端可能是乱序或者重复的。TCP协议会对收到的TCP报文进行重排、整理,确保顺序正确。TCP报文段所携带的应用程序数据按照长度分为两种:交互数据和成块数据对于什么是粘包、拆包问题,我想先举两个简单的应用场景:对于第一种情况,服务端的处理流程可以是这样的:当客户端与服务端的连接建立成功之后,服务端不断读取客户端发送过来的数据,当客户端与服务端连接断开之后,服务端知道已经读完了一条消息,然后进行解码和后续处理...。对于第二种情况,如果按照上面相同的处理逻辑来处理,那就有问题了,我们来看看第二种情况下客户端发送的两条消息递交到服务端有可能出现的情况:第一种情况:服务端一共读到两个数据包,第一个包包含客户端发出的第一条消息的完整信息,第二个包包含客户端发出的第二条消息,那这种情况比较好处理,服务器只需要简单的从网络缓冲区去读就好了,第一次读到第一条消息的完整信息,消费完再从网络缓冲区将第二条完整消息读出来消费。第二种情况:服务端一共就读到一个数据包,这个数据包包含客户端发出的两条消息的完整信息,这个时候基于之前逻辑实现的服务端就蒙了,因为服务端不知道第一条消息从哪儿结束和第二条消息从哪儿开始,这种情况其实是发生了TCP粘包。第三种情况:服务端一共收到了两个数据包,第一个数据包只包含了第一条消息的一部分,第一条消息的后半部分和第二条消息都在第二个数据包中,或者是第一个数据包包含了第一条消息的完整信息和第二条消息的一部分信息,第二个数据包包含了第二条消息的剩下部分,这种情况其实是发送了TCP拆,因为发生了一条消息被拆分在两个包里面发送了,同样上面的服务器逻辑对于这种情况是不好处理的。我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。发生TCP粘包、拆包主要是由于下面一些原因:既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:写了一个简单的 golang 版的tcp服务器实例,仅供参考:例子参考和推荐阅读书目:注释:eg.

tcp的报文格式
自己算。。。

tcp报文格式及每个t字段的含义
首部固定部分各字段的意义如下: 源端口和目的端口:各占2个字节,分别写入源端口号和目的端口号。序号:占4个字节。序号使用mod运算。TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。故该字段也叫做“报文段序号”。确认序号:占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号。若确认序号=N,则表明:到序号N-1为止的所有数据都已正确收到。数据偏移:占4位,表示TCP报文段的首部长度。注意,“数据偏移”的单位是32位字(即以4字节长的字为计算单位)。故TCP首部的最大长度为60字节。保留:占6位,保留为今后使用,目前置为0;紧急URG:当URG=1,表明紧急指针字段有效。这时发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。确认ACK:当ACK=1时,确认字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。推送PSH:接收方TCP收到PSH=1的报文段,就尽快地交付给接收应用进程,而不再等到整个缓存都填满了后再向上交付。复位RST:当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立运输连接。同步SYN:在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。故SYN置为1,就表示这是一个连接请求和连接接收报文。终止FIN:用来释放连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。窗口:占2个字节。窗口值作为接收方让发送方设置其发送窗口的依据。检验和:占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP数据报一样,在计算检验和时,也要在TCP报文段的前面加上12字节的伪首部。伪首部的格式与UDP用户数据报的伪首部一样,但要将伪首部第四个字段中的17 改为6(协议号),把第5字段中的UDP长度改为TCP长度。 紧急指针:占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数。

tcp数据包格式中没有包括的是
这也没有选项哇,不包括的东西有很多。TCP报文由首部和数据两部分组成。首部一般由20-60字节(Byte)构成,长度可变。其中前20B格式固定,后40B为可选。因为,TCP报文还得传给下层网络层,封装成IP包,而一个IP包最大长度为65535,同时IP包首部也包含最少20B,所以一个IP包或TCP包可以包含的数据部分最大长度为65535-20-20=65495B。TCP报文中数据部分是可选的,即TCP报文可以不包含数据(同理IP包也可以不包含数据)。不含数据的TCP报文通常是一些确认和控制信息类的报文,如TCP建立连接时的三次握手和TCP终止时的四次挥手等。1、源端口号(SourcePort)长度为16位,指明发送数据的进程。2、目的端口号(DestinationPort)长度为16位,指明目的主机接收数据的进程。3、序号(SequenceNumber)记录发送次数也称为序列号,长度为32位,序号用来标识从TCP发送端向接入端发送的数据字节流进行编号,可以理解成对字节流的计数。4、确认号(AcknowledgementNumber)长度为32位,确认号包含发送确认的一端所期望收到的下一个序号。确认号只有在ACK标志为1时才有效。5、首部长度(数据偏移)长度为4位,用于表示TCP报文首部的长度。用4位(bit)表示,十进制值就是[0,15],一个TCP报文前20个字节是必有的,后40个字节根据情况可能有可能没有。如果TCP报文首部是20个字节,则该位应是20/4=5。6、保留位(Reserved)长度为6位,必须是0,它是为将来定义新用途保留的。给QOS预留用的7、标志(CodeBits) 长度为6位,在TCP报文中不管是握手还是挥手还是传数据等,这6位标志都很重要。

本文由 在线网速测试 整理编辑,转载请注明出处。