文章制作不易,望各位大佬多多点赞,球球各位啦!!!!
目录
1.TCP的简介
2.TCP协议中部分数据的理解
1.端口号
2.序列号
3.四位首部长度
4.6位保留位
5. 16位校验和
6.数据(TCP的载荷)
3.TCP如何实现可靠传输
1.确认应答
2.超时重传机制
3.连接管理
三次握手
四次挥手
小结
4.TCP如何优化因可靠传输造成的效率降低
1.滑动窗口
流量控制
拥塞控制
(PS:流量控制和拥塞控制在实际情况下是联合使用的,会取他们俩之间的最小值)
2.延时应答
3.捎带应答
5.TCP对特殊异常场景的解决
1.进程崩溃
2.主机关机
3.主机掉电
4.断网
6.TCP因为面向字节流引出的粘包问题
总结:
1.TCP的简介
TCP全称传输控制协议(Transmission Control Protocol)。它是传输层的协议。
TCP拥有以下属性:。
1.TCP是有连接的
2.TCP是可靠性传输
3.TCP是面向字节流的
4.TCP是全双工的
TCP协议相较于其他协议来讲最主要的是实现了一个非常可靠的传输。
以下图中很大一部分的空间都是为了实现可靠传输。那接下来我们开始了解TCP吧!
2.TCP协议中部分数据的理解
1.端口号
TCP中记录了源端口号和目的端口号,它们分别都是16位的。这是数据传输到目的地的基础。很好理解。这就相当于去网吧点外卖外卖,你的座位号。没有座位号他就不可能送到你的面前。
2.序列号
因为TCP是面向字节流的,所以每一个字节都会有自己的编号,以便接下来的传输以及排序,后面我们会讲到,这个序列号还可以解决流量控制里的收未收到问题。序列号的工作原理就是发送端发送信息到接收端,接收端就会返回一个应答报文,应答报文里的确认序号就会向发送端索要序列号加一的数据。
而发送端的序列号一般也不会是1,在后面的连接管理中我们会讲到。
3.四位首部长度
这里和UDP协议不同,UDP是16位报文长度,而TCP是首部长度。这里的首部长度是指报头信息的长度。那有的长的帅的哥们就会问了“不是哥们儿?看着上面的图,不算上选项的大小都已经有20字节的大小,但是4个bit最大就只有15个字节的大小,这怎么够放?”。这是因为他代表的首部长度是乘以4之后的长度。也就是说首部最长是60个字节,那也就是说选项的大小必须是4的倍数,最大只能是40byte。
4.6位保留位
这里的六位保留位就是为了防止因为某些字段限定了大小导致在未来使得某些功能实现的很麻烦。
就如UDP就是因为最大报文长度为16个字节导致传送的信息最大就只有64kb。而现在很多信息都已经超过了64kb大小,所以使用UDP就会产生一些很麻烦的事情,就比如说要发送大于64kb的信息,那么就要程序员在应用层方面进行拆包和组包,对我们的开发效率产生了很大的影响。
5. 16位校验和
我们发送数据的时候为了防止我们发送的数据因为外界环境影响而改变,TCP引入了校验和机制。在发送数据之前,TCP会计算出整个报文的校验和,并且将这个校验和存到16位校验和中。待数据发送到接收端时,接收端会重新计算校验和,如果相同,那么就没什么问题,如果不同则接收端会抛弃这个数据并且触发重传功能。当然校验和的方式也不止一种,最常见的就是CRC(循环冗余校验),MD5(Message-Digest Algorithm 5),SHA1(安全哈希算法1) 。其中MD5还被广泛用于密码学(它具有以下三点性质:1.无论什么数据结果都是定长的。2.分散性,数据只要发生一点点改变结果就会发生很大的改变。3.不可逆性,他的运算是一个单向的过程,目前的算力基本无法对MD5进行解密)。
6.数据(TCP的载荷)
因为TCP是面向字节流的,所以数据的大小本身没有什么限制。但是他会受像MTU(最大传输单元) ,MSS协商(三次握手之后确定的最大窗口大小),操作系统限制和应用程序设置。
3.TCP如何实现可靠传输
TCP协议最核心的就是它实现了可靠传输。可靠传输不是指信息百分之百能传输到接收端,而是尽量保证数据能够完全有序地无错地传到接收端去。那为什么不是百分之百呢?这是因为TCP再怎么厉害也只是传输层地协议,在它的更底层出现问题,就比如物理层的光纤断了,无论你这个协议写的再好,也不可能传到接收端了。那我们大概了解了以下什么叫可靠传输,我们再了解一下TCP协议是怎么实现可靠传输的。
1.确认应答
确认应答机制就是当我们的发送端A如果想要发送一条信息给接收端,它就会将这个信息通过网络传送到接收端B。但是这个时候A并不知道B到底收到信息了没有,所以为了让A知道B接收到了信息,B会发送一条没有商业性数据的报文(应答报文)ACK(acknowledge)来告诉A它已经收到了这条信息。
确认应答机制是TCP实现可靠传输的核心机制,后面很多机制都是建立再确认应答之上的,所以确认应答机制很关键。
这里的ACK数据用报文段里的ACK标志符表示
2.超时重传机制
如果在发送端发送信息到接收端的途中数据应为某些原因丢失了。此时接收端没有接收到信息,就不会返回ACK,那丢了怎么办呢?这时候TCP就引入了超时重传机制。在发送端没有接收到ACK一段时间后,那么这时候发送端就认为是自己的数据丢了,它就会重新发送这段数据。如果过一段更长的时间还没收到,那就再次重发,等待时间就会更久,如此循环。在一定时间内一直没有收到ACK,那么接收端就会认为这个连接已经挂了,接收端就会自行断开连接。这里还有一个问题,如果第一条数据只是在路途中耽搁了一会,但是这时候已经触发了超时重传了,此时两条消息都会发到接收端里去,这时候怎么办呢。制定TCP协议的大佬早就想到了这个问题,他们为TCP设置了一个缓存区。发送来的数据都会先在缓存区里排队,在缓存区内,TCP就会对这些数据进行去重操作。那怎么分辨是否是同一串数据呢,这里我们就会用到前面讲的序列号了,TCP会在缓存区内把相同序列号的数据去掉。也就达成了去重的效果。当然缓存区还有一些功能,就比如我们在后面会讲到的自动排序功能。
3.连接管理
我们在前面说到,TCP协议是带连接的。那连接是什么呢?在这里连接是抽象的概念,表示发送端和接收端互相记录了彼此的信息。就像是结婚一样,领结婚证就是彼此建立连接,双方都留下了结婚证,也就是彼此的信息,当要离婚时,也得双发拿着结婚证去民政局办理。
说到连接就逃不开连接管理的核心内容,3次握手和4次挥手(虽然英文都是shake hands,但是中文翻译成这样更好理解,毕竟握手表示见面,挥手表示离别)。
三次握手
三次握手的流程可以用下图来描述:
A(请求连接端)向B(接收端)发送SYN(同步报文)来请求B的连接,这时候B就会返回ACK应答报文表示已接收到请求同意连接,然后B会向A发送建立连接的请求,A就会返回ACK表示收到请求同意连接。这时候连接就建立成功了。这时候有的长得帅的大哥就会问了“这里明明是4个步骤啊,怎么会是3次挥手呢?误导读者是吧,你给我去SPA!!!”。我看你是完全不懂哦!这里B返回的ACK和SYN可以合并成一个,这样就可以减少网络传输的数据量了,加快了连接的效率,那么3次握手的图就变成了下面这样:
这不就刚好符合三次嘛。那这三次握手有什么意义呢?我们可以对比我们平常打电话的时候:
映射到三次握手上就是:
1.投石问路,确保网络状态是好的
2.确保发送端和接收端的发送功能和接收功能是好的
3.可以商量一些数据和协议的细节内容,如序号从什么时候开始,窗口的最大大小。
这里我们着重解释一下为什么要商议序列号从什么时候开始。请看大屏幕:
在Java中ServerSocket建立好时就已经服务器就处于LITEN状态了,建立好之后服务器就处于ESTABLISH阶段了
四次挥手
相反于三次握手的建立连接过程,四次挥手也就是断开连接的过程。大致原理如下图:
过程与3次挥手差不多,我们这里就不在过多叙述。但是这里有个小问题,B这里的ACK和FIN为什么没有合并在一起呢?这是因为在一般情况下A请求断开连接之后B会返回ACK,但是此时B这边一般还要执行一些收尾工作,所以不会急着发送FIN,而是善后工作结束之后再发。所以我们这里就不能直接合并在一起。但是也有能合并的情况,我们会在后面的延迟应答和捎带应答里面会讲这种情况。
这里还有一个比较重要的断开连接中间状态。如图:
小结
TCP完成这些可靠传输的操作要付出很多效率的代价。
4.TCP如何优化因可靠传输造成的效率降低
上面我们讲到TCP为了可靠付出了很大的代价,所以TCP为了弥补效率的降低加入了很多优化的机制(PS:虽然优化能大大提升传输效率,但是再怎么优化速度也不可能比过TCP这种不可靠传输)。接下来我们来讲讲这些优化措施。
1.滑动窗口
滑动窗口就是让TCP发送数据不再是一应一答,而是多问多达。先发送出一条消息看看接收缓冲区还有多少空间(后面会讲),然后根据接收缓冲区的大小一并把数据发送过去如图:
当服务器接收到一个数据之后就会返回ack,如若其中一个ack丢了也没事,下一个ack如果能返回就能代表我之前的数据都已经传输到达。就比如下一个是2001这条ack丢失,但是返回了3001ack,那么就表明服务器之前的数据都接收到了。当然如果是第一条丢了,发送端这里就会触发超时重传,重新发送。如果是传输途中数据丢了,那么服务器也会一直索要当前序列号数据。就比如2001丢了,发送端还发送了3001~4000,那么服务器不会读取这条数据而是把它留在缓冲区,然后一直返回ack索要2001这条信息。这里一次性发送的数据多少就是窗口的大小。那为什么叫滑动窗口呢,滑动体现在如果第一条信息解决了,服务器就会继续往后读取信息,就像窗口在滑动一样。那么是不是窗口越大越好呢?且不说有窗口大小的限制。一次性传输的数据过多也会导致数据在发送途中容易丢失。所以这时候我们就引入了流量控制。
流量控制
流量控制能动态的控制窗口的大小,在信息发送到服务器时,服务器的ack还会返回缓冲区的大小(用缓冲区的大小来衡量服务器处理数据的能力)。这时候发送端就会控制这次发送的数量,当缓冲区满了时,发送端就不会发送商业数据了。但是为了知道服务器的缓冲区是否已有空间继续发送信息,发送端会每隔一段时间就发送探测数据报来让服务器返回缓冲区的空间。
拥塞控制
流量控制在服务器角度考虑了发送窗口的大小,而我们还需要考虑路径的数据处理能力。不同的路径上的数据处理装置处理数据的能力不一样,这样我们就很难直接判断出发送的数据多少。所以TCP采用拥塞控制,用实验的方法来判断当前的窗口大小。那么大致上就是先缓慢的发送数据,然后速度不断地变快,当丢包时,就减小窗口大小,当不丢包一段时间时又加大窗口大小,如此循环,达到动态平衡。以下是拥塞控制窗口大小和时间的关系图:
但是这种方案在现在已经落伍了,因为当丢包时一下子把窗口大小变到最小然后再重新增大到设定值很影响效率,所以现在的方案大部分是如下图所示:
(PS:流量控制和拥塞控制在实际情况下是联合使用的,会取他们俩之间的最小值)
2.延时应答
延时应答机制是为了优化滑动窗口的。如若没有延时应答,发送端发送信息到接收端,接收端还来不及处理数据就要返回ACK,这导致缓存区可能一下就满了,所以为了给接收端处理数据的时间,TCP让接收端收到数据之后过一段时间再返回ACK,这样数据就先被接收端处理一会之后再返回ACK。这样返回的缓存区空间就会变大,就可以请求更大的窗口。
3.捎带应答
捎带应答建立在延时应答之上,当有多条数据发送过来时,如果每个ACK都返回就会给网络传输带来压力。正因为接收端有延时应答,可以先等待一会,等待一些数据发送到了再合并把ACK返回。但是这个等待时间时有上限的,超过时间必须发送ACK,否则会影响TCP的传输,还可以引起发送端的超时重传功能。
正因为有捎带应答,有时候要断开连接,断开连接的发起者A发送FIN到被断开者B时,B会等一会会再返回ACK,如果此时进程的善后工作刚好做完了,那么就会触发捎带应答机制,把ACK和FIN一同返回到A,那么A就能返回ACK与B正常断开连接了。如图:
这就是捎带应答。
5.TCP对特殊异常场景的解决
1.进程崩溃
如果进程崩溃,进程就会结束,因为TCP的连接和进程时相互独立的,所以进程崩溃的这一端的服务器就会发送FIN,接收端会返回ACK和FIN,发送端照样会返回ACK。也就是正常的四次挥手过程。
2.主机关机
如果主机关机,进程照样会先结束,此时关机这一端就会先发出FIN,此时接收端就会返回ACK和FIN,如果此时系统还没关机,那么就会返回ACK,也就是执行完四次挥手才断开了。但如果此时系统已经关机,那么接收端就一直等不到ACK,这时就会触发超时重传,因为已经关机,所以一直没人处理ACK,重传几次后就会进入复位重置状态,但是还是复位不了啊,毕竟关机了嘛,所以最后实在没辙,接收方就放弃了这个连接。
3.主机掉电
如果主机掉电,进程和系统都直接没了,如果此时对端正在发送消息,但是一直没有回应ACK,就会触发超时重传,复位重置,和断开。如果这个时候对端正在接收信息,一直没有动静,此时TCP的另一个机制就出现了,叫做心跳包机制。对端会不定时的向客户端发送探测包,如果没有回应多次,就会视为这个连接已经挂了,那么对端自然会断开连接。
4.断网
和掉电类似,如果断网了,客户端和服务器都是可以运行的,只不过消息发不过去,客户端会触发超时重传机制,复位重置,然后断开。对端会发送心跳包,没回应次数多了也会自动断开。
6.TCP因为面向字节流引出的粘包问题
因为tcp是面向字节流的,所以应用程序只会在缓冲区里看到一个个字节,应用程序又不知道怎么把这些字节分开,这时候就出现了粘包问题。如图:
这时候就需要我们程序员加一个分隔符或者定一个协议(如xml,json,protobuff),让应用程序能够正确的从缓存区中读取数据。
如加一个'\n'如图:
这样我们就可以让应用程序分清楚怎么读取数据。
其中xml,json分别是下图的一种表达形式,而protobuff是把数据转化成二进制进行传输,这里我们不过多讲解。
总结:
TCP真的是一个值得深究的协议,在RFC中对他制定的规则有80多页,我讲的虽然是重点但是也只是冰山一角。
这篇文章写的真的非常非常非常非常辛苦,希望各位大佬们能多多点赞关注支持一下,球球你们啦!!!!