电子说
7、TCP的连接与断开
7.1、TCP的连接建立
7.1.1、TCP连接建立过程中要解决的3个问题
1.要使一方确定对方的存在。
2.双方之间协商一些参数比如说滑动窗口的大小,时间戳选项等等。
3.能够对运输实体资源(缓存大小,连接表中的项目)进行一些分配。
7.1.2、三次握手建立连接
在建立连接之前,Client处于CLOSED状态,而Server处于LISTEN的状态。
1.第一次握手:客户端主动给服务端发送一个SYN报文,并携带自己的初始化序列号一起发送给服务端。此时客户端处于一个SYN_SEND的状态。
2.第二次握手:服务端收到客户端发来的SYN报文之后,就会以自己的SYN报文作为应答,然后将自己的初始化序列号发送给客户端,并且会将客户端的初始化序列号+1作为自己的ACK值发送给客户端,以表示自己已经收到了客户端的SYN报文。此时服务端处于一个SYN_RECV的状态。
3.第三次握手:客户端收到服务端发来的SYN报文之后,会把服务端的初始化序列号+1作为ACK值发送给服务端,用来表示自己已经收到了服务端发来的SYN报文。此时客户端处于一个ESTABLISHED的状态。
7.1.3、为什么两次不行
一开始A向B发出建立连接的请求但是这个报文超时了,A又向B发送了一个请求连接报文然后通过两次握手建立连接传送数据最后释放连接。但是这时候,超时的报文迟到发送他们又会建立连接然后服务端等待发送数据,但是这时候客户端已经没有数据可发了。
7.1.4、三次握手的原因
可靠性要求之一(确认、序号、重传):起始数据字节编号协商
如图所示它主要是为了起始数据字节编号协商1是我发送的报文序号是456,2是我接受到了你下次从457开始发,3是好的我准备接收124。如果没有这个3号报文B端就不知道下次从哪开始发。
7.1.5、三次握手建立TCP连接的各种状态
首先服务端开始处于监听状态监听客户端发来的请求,客户端发送建立连接的请求之后处于SYN-SEND状态,服务端发送确认报文之后处于SYN-RCVD状态,当客户端发送确认报文给服务端客户端处于ESTAB-LISHED(连接建立状态)服务端接收到报文后进入ESTAB-LISHED状态。
7.1.6、三次握手可以携带数据吗?
大概很多人的正常思维都是在三次握手过程中是不可以携带数据的,其实不然,在第三次握手的过程中是可以携带数据滴;换句话说就是,在第一、第二次握手过程中不可以携带数据的,但是在第三次握手过程中是可以携带数据的。
那为什么会这样呢?大家不妨大胆的猜测猜测....
因为是网络连接,就会涉及到网络安全的因素;大家想想,如果在第一次握手的时候就可以携带数据,那么要是有人蓄谋已久恶意攻击服务器,在第一次握手中的SYN报文注入大量数据,攻击者根本就不考虑服务器的发送接收能力,然后就疯狂重复发SYN报文,这就会让服务器花费大量的内存和时间去处理这些报文,所以如果在第一次握手就放数据的话,就会让服务器更加容易受到攻击。
7.2、TCP连接释放
1.客户端向服务端发送一个报文FIN为1,序号为u然后进入FIN-WAIT1状态。
2.服务端向客户端发送确认报文序号为v,确认序号为u+1然后进入CLOSE-WAIT状态。
3.客户端收到服务端发回的确认报文之后进入FIN-WAIT2状态此时客户端连接已经关闭客户端无法向服务端传送数据。
4.然后服务端被动关闭它向客户端发送一个FIN为1的报文段要求释放服务端到客户端的连接。进入LAST-ACK等待客户端发送最后一个ACK报文。
5.客户端发送最后一次挥手确认报文然后进行closed,服务端直接CLOSED。
6.客户端要等待2MSL才CLOSED。
当不按套路出牌时返回RST比如上来没建立连接服务端给客户端来一个ACK或FIN。
7.2.1、为什么客户端要等2MSL才关闭
1.因为服务端要保证TCP全双工通信连接都能正确关闭,因为如果服务端没收到ACK那么就会再发一次FIN那么客户端如果关闭则无法回复ACKServer就会收到RST而不是ACK。所以为了让服务端能正确的收到ACK报文确保连接正确关闭所以要等一会再关。
2.一旦客户端直接进入CLOSED很有可能端口号跟之前相同然后上一次连接中有些数据滞留在网络,当你再建立连接时这些老的数据包会和新的数据混淆等待2MSL基本上可以让这些老数据消失。
7.2.2、保活计时器
•当建立连接之后一旦断网了,连接空闲时间达到两个小时以上服务器自动发送探测报文段,若发送了10个报文段(每个相隔75秒)还没有响应,就假定客户除了故障,因而终止连接。
•TCP计时器总结:TCP发送报文计时器,超时重传计时器,保活计时器,持续(0窗口探测计时器),时间等待计时器(2MSL);
7.3、TCP黏包
7.3.1、什么是TCP黏包?
在以往的网络基础学习中,可能会客户端连续不断向服务端发送数据包的时候,服务端接收的数据会出现两个数据包黏在一起的情况。
•TCP是基于字节流的可靠传输,在应用层和传输层之间的数据交互是大小不等的数据块,但是TCP就会将这些数据块看成是一串一串没有结构的字节流,并且没有边界。
•学习TCP结构的时候也可以看出,它的首部没有表示数据长度的字段。
所以,在使用TCP进行数据传输的时候,就会有黏包或者拆包的现象发生。一个数据包中包含了发送端发送的两个数据包的信息,那么就把这种现象叫做黏包。接收端在收到这两个数据包的时候,要么是不完整的,要么是多出来一块,这种情况就是发生了黏包和拆包。
7.3.2、TCP黏包是怎么产生的?
1.发送方产生黏包
因为在采用TCP传输数据的客户端和服务端是保持一个长链接的状态,双方在不断开的状态下,是可以一直传输数据的。如果发送的数据包非常小的时候,那么TCP默认就会启用Nagle算法,将这些非常小的数据包进行合并发送(这个合并发送过程就是在发送缓冲区中进行的),发出来的数据就会是一个黏包的状态。
2.接收方产生黏包
数据在到达接收方的时候,是从网络模型的下方传递至传输层,传输层的TCP协议就会将这些数据放置到接收缓冲区,然后由应用层主动获取,那么这个时候就会出现在我们程序调用的读取数据函数不能及时的把缓冲区中的数据拿出来,下一个数据的一部分就又到缓冲区中,当我们读取的时候就是黏包。
7.3.3、怎么解决黏包和拆包?
1.FixedLengthFrameDecoder
对于使用固定长度的粘包和拆包场景,可以使用FixedLengthFrameDecoder,该解码器会每次读取固定长度的消息,如果当前读取到的消息不足指定长度,那么就会等待下一个消息到达后进行补足。
2.LineBasedFrameDecoder与DelimiterBasedFrameDecoder
对于通过分隔符进行粘包和拆包问题的处理,Netty提供了两个编解码的类,LineBasedFrameDecoder和DelimiterBasedFrameDecoder。这里LineBasedFrameDecoder的作用主要是通过换行符,即\\n或者\\r\\n对数据进行处理;而DelimiterBasedFrameDecoder的作用则是通过用户指定的分隔符对数据进行粘包和拆包处理。
3.LengthFieldBasedFrameDecoder与LengthFieldPrepender
这里LengthFieldBasedFrameDecoder与LengthFieldPrepender需要配合起来使用,其实本质上来讲,这两者一个是解码,一个是编码的关系。它们处理粘拆包的主要思想是在生成的数据包中添加一个长度字段,用于记录当前数据包的长度。
4.自定义粘包与拆包器
对于粘包与拆包问题,其实前面三种基本上已经能够满足大多数情形了,但是对于一些更加复杂的协议,可能有一些定制化的需求。对于这些场景,其实本质上,我们也不需要手动从头开始写一份粘包与拆包处理器,而是通过继承LengthFieldBasedFrameDecoder和LengthFieldPrepender来实现粘包和拆包的处理。
8、UDP主要特点
1.不需要建立连接。
2.尽最大努力交付。
3.面向报文交付。
4.没有拥塞控制。
5.支持多种交互通信。
6.首部开销小仅有8字节。
7.应用程序必须选择合适的报文如果报文太长则需要IP分片,太小降低效率。
8.1、运用场景:
1.可以重复请求信息的情况下例如:RIP,DNS,DHCP等
2.一次性传小量数据的应用
3.实时应用
4.多媒体应用。
8.2、UDP首部信息
1.源端口(2)
2.目的端口(2)
3.长度(2)
4.检验和(2)(首部+伪首部+数据)
这个检验和是交给上层应用程序检查的,它就相当于你拆快递先检查收货地址(IP地址),再检查是不是你的名字(端口),再检查里面收件是不是错的(里面的数据)。
以上就是我对TCP、UDP相关的总结;如果对您有所帮助的话,那就是我花时间整理这篇文章最大的意义了。
全部0条评论
快来发表一下你的评论吧 !