文章目录
传输层
传输层的功能:负责两个进程或者应用程序之间的数据传输
UDP协议
协议格式
16位源端-对端端口:用于识别通信两端进程
16位数据报长度:能够描述的最大数字为65535,即一个udp报文总大小不能超过64K
16位校验和:采用二进制反码求和算法,用于校验接收到的数据与发送方的数据是否一致
协议特性
无连接:通信时不需要建立连接,只需要知道对方的地址就可以直接发送数据
不可靠:不保证数据安全有序地到达对端
面向数据报: 面向数据报的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。
UDP数据包的发送流程:
- udp传输时,sendto发送的数据会被放到发送缓冲区中,并且udp协议并不会等待,而是直接对数据进行头部封装,进行发送
- udp传输时,收到的数据会被放到接收缓冲区中,解析它的头部信息后,获取数据报长度,从缓冲区中读取对应长度的数据。所以udp协议保证了数据是整条交付的,不会出现半条或多条交付。
对编程影响
- 不保证安全到达:会在应用层使用tcp的一些机制来保证安全
- 不保证有序到达:需要再应用层进行包序管理
- udp报文有最大长度限制:报文最大长度小于64K,因此发送大块数据时,需要在应用层进行数据包的分包发送
- udp实现的是整条交付:因此接收方必须定义一个足够大的接收缓冲区,来接收整条数据
TCP协议
协议格式
16位源端-对端端口:识别通信两端
32位序号-确认序号:进行包序管理,保证tcp数据的交付是有序的
4位首部长度(最大值1111=15):以4字节为单位,表示tcp的头部长度最大有60字节,最小长度20字节
6位标志位:
URG:紧急指针有效位
ACK:确认应答标志位
PSH:接收方应尽快将报文发送给应用方
RST:重置连接报文位
SYN:建立连接请求标志位
FIN:断开连接请求位
16位窗口大小:进行流量控制,实现滑动窗口机制(防止因为数据发送过快或缓冲区空间较小而造成的数据丢失)
16位校验和:校验接受的数据与发送的数据是否一致
协议特性
TCP协议的三大特性:面向连接、可靠传输、面向字节流
面向连接:连接管理+状态管理
三次握手
- 客户端向服务端发送简历连接请求,发送syn=1(建立连接请求标志位syn置为1),然后客户端进入SYN_SENT状态(当前客户端请求已发送,等待服务端确认回复)
- 服务端监听到连接请求后新建一个套接字,进行确认回复发送ack=1,syn=1,然后这个新建套接字进入SYN_RCVD状态(表示收到请求并且已经回应,等待客户端回复)
- 客户端再进行一次确认回复,发送ack=1,然后进入ESTABLISHED(进入连接已确认状态,可以进行正常的数据传输);服务端最后一次收到确认回复后也进入ESTABLISHED状态,此时双方已经建立连接。
四次挥手
调用close/shutdown(x,WR);发送FIN包,发起断开连接的请求
int shutdown(int sockfd,int how);-----关闭一个套接字的读或写操作,不释放资源
只有在shutdown(x,WR);关闭写操作时才会发送FIN包
close();-----关闭操作并且释放资源
- 主动关闭方调用close/shutdown(x,WR);发送FIN包,并停止向对方发送数据,进入FIN_WAIT1(终止等待1)
- 被动关闭方接受FIN包并进行确认回复ACK=1;进入CLOSE_WAIT状态,此时的TCP处于半关闭状态,主动方进入FIN_WAIT2(终止等待2)
- 被动方在发送完剩余的数据后调用close/shutdown(x,WR);发送FIN包,进入LAST_ACK(最后确认)
- 主动方对被动方的FIN包进行确认回复,进入TIME_WAIT状态,会在等待一段时间后进入closed状态;而被动方在收到ACK确认后就直接进入closed状态
常见面试题:
为什么握手是三次,而挥手是四次?
-
TCP是一个全双工通信(可以同时接收和发送数据),通信两端都必须确认对方是否具有数据收发的能力,所以ack和syn可以一起发送,四次就没必要了,两次也不行,不能保证安全,所以握手是三次。
-
挥手时,有可能被动关闭方还会继续发送数据,因此这种情况下就不能直接发送fin,而是等待上层用户不在发送数据了,再调用close/shutdown才会发送fin包。因此,被动关闭方的ack和fin无法一起发送,所以挥手需要四次。
如果三次握手失败了,两端会如何处理?
- 客户端会重传syn请求
- 服务端在回复了ack+syn后,如果长时间得不到客户端的回复,就会发送rst重置连接报文,然后释放新建的套接字。
TIME_WAIT有什么用?
TIME_WAIT是主动关闭方在最后一次ack回复后进入的状态 。
如果主动关闭方在最后一次ack后直接释放资源,那么有可能后序新的客户端会使用原来被释放掉的地址,而此时如果上次ack发生了丢失则会重传fin,那么这时的fin会被新的客户端接收到,影响了新客户端的正常工作。
time_wait等待2msl时间之后,释放资源,等待2msl时为了保证能够对重传的fin进行处理,以及保证本次通信的所有数据都消失在网络中,不会对后续的新建连接造成影响。
1msl默认是60秒,并且可以修改
如果一台主机上出现了大量的TIME_WAIT是什么原因?如何解决
time_wait是套接字主动关闭连接,最终所进入的状态
因此,一套主机中出席那大量的time_wait意味着大量的主动地关闭了套接字
如何解决:
- 减少time_wait等待时间
- 使用套接字选项:地址复用
int setsocketopt(int fd,int level,int optname,void* optval,int optlen);
如果一台主机上出现了大量的CLOSE_WAIT是什么原因?如何解决
CLOSE_WAIT是被动关闭方,在收到FIN包进行ACK回复之后所进入的状态,等待被动方的上层用户调用close/shutdown(x,WR)发送FIN包的状态。
如果主机上出现了大量的CLOSE_WAIT,意味着我们在代码中没用调用close/shutdown(x,WR)关闭套接字。
tcp连接保活机制
通信两端在长时间没有数据通信的抢矿下,服务端每隔一段时间会向客户端发送一个保活探测数据包(要求对方进行回复),如果连续多次没有收到回复, 则认位连接已经断开。
保活机制的底层默认值:
这三个参数的含义:通信两端如果连续7200秒没有数据通信,则服务端每隔75秒向对方发送一个保活探测数据包,如果连续9次未收到回复,则认为连接断开。
这三个参数也可以通过套接字选项操作进行更改。
可靠传输:保证数据有序、安全到达对端
- 面向连接
- 协议字段中的序号与确认序号,进行包序管理,实现有序传输
如何实现包序管理 :
假设现在客户端要向服务器发送3条1024字节长度的信息,但是第二条信息发生了丢失,那么在收到第三条信息时,服务端就会通过序号来判断第三条数据应该存放在缓冲区的哪个位置,而原本应该存放丢失数据的缓冲区段依然是空的
- 确认应答机制:接受方对收到的每一条数据进行确认回复(如果中间有数据发生丢失,则之后不会再进行确认应答,直到该丢失的数据被重新接收到)
- 超时重传机制:超过指定时间都没有收到确认回复,就会对该丢失的数据进行重传
- 协议字段中的校验和:校验数据一致性(如果数据不一致,则直接丢弃,并发送重传请求)
- 避免丢包:
发送方发送数据过多、过快,导致接收方接收缓冲区满溢导致丢包
解决方案:滑动窗口机制进行流量控制;依赖协议字段中的窗口大小字段实现;传输过程中,网络状态变差,导致大量丢包重传
解决方案:拥塞机制----以一种慢启动快增长的形式进行传输补充知识:滑动窗口机制中发生数据丢失时的处理方案
停等协议:收到确认回复后才会发送下一条
回退n步协议:从丢失的数据开始重新进行传输
选择重传协议:哪条数据丢失就重传哪条
- TCP性能的提升:避免无谓的性能损失
确认序号:是告诉发送方确认序号之前的所有数据都已经接收成功,避免因为中间的确认回复丢失而导致重传
快速重传机制:如果在接收多条数据时,先收到后发的数据,则认为前边的数据有可能丢失,则接收方就会以一定的时间间隔连续发送三条该丢失数据的序号,表示该数据需要重传- 连发三次是为了避免因为网络波动而引起的数据发送延迟。
- 快速重传的目的是为了减少超时等待时间。
延迟
延迟应答机制:接收方接收到数据后,延迟确认回复
延迟应答是为了,在延迟期间,上层有可能将数据取出,尽量保证窗口大小,保证传输吞吐量
捎带应答机制:将确认回复的信息,放到即将要发送的数据报报头中,捎带一起传输给对方。
尽可能减少纯报头的确认回复(一个报头至少20字节)
总结:tcp如何实现可靠传输
- 可靠传输:面向连接、包序管理、确认应答、超时重传、校验和
- 避免丢包:滑动窗口机制、拥塞机制
- 提高新跟那个:延迟应答、捎带机制、延迟发送
提问:udp如何实现可靠传输?
答:在应用层通过tcp实现可靠传输的机制来实现。
面向字节流:
面向字节流:基于连接、可靠的、有序的、双向的一种字节流(以字节为单位)的传输方式
tcp要发送的数据都会被放到发送缓冲区中,通信时tcp会从缓冲区中取出合适大小的数据(不大于mss),然后封装头部进行发送;
不限制上层的发送以及接受数据的大小,数据会在缓冲区堆积;
这样设计的好处:传输比较灵活
坏处:会产生粘包问题
TCP的粘包问题
粘包问题:将多条数据当作一条数据进行处理,无法分辨数据的边界
粘包的原因:tcp不维护数据的边界
解决方案:程序员应该在应用层进行数据边界管理
具体方案:
1. 特殊字符限制边界,例如http中的 \r\n ;缺点:数据中不能含有该特殊字符
2. 数据定长:将一条数据设置为定长(以最长的那一条数据为基准),不够则补全;缺点:效率低
3. TLV-----应用层头部加上数据长度字段:type length value,在发送tcp包时,用type标明数据类型,length标明数据长度,value代表要发送的数据。
注意:udp不会出现粘包问题!!!