一、前提介绍
(1)什么是TCP连接、什么是半包、什么是粘包
TCP是一种面向连接的、可靠的传输层通信协议,通过序列号和确认消息来保证数据传输的可靠性。也由于TCP传输的是字节流,并没有明确的边界指示,这导致了粘包和半包问题的出现。粘包问题指的是本应分开传输的多个数据包被合并成一个数据包进行传输,而半包是指一个数据包在传输过程中被分成多个小包进行传输
(2)MTU、MSS、IP分片介绍
MTU(Maximum Transmission Unit)是指在数据链路层网络能够传输的最大数据包大小,以字节为单位。它决定了发送端一次能够发送报文的最大字节数
MTU = 1518 字节 - (14 字节头部和 4 字节 CRC)= 1500
MSS(Maximum Segment Size)是TCP协议的一个选项,用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度。MSS记录TCP数据的字节数而不包括其他相关的TCP与IP头部。一般的,MSS=MTU-TCP头部大小-IP头部大小
IP分片:IP分片是将一个IP分片成多个IP分片,每个IP都是一个IP数据包,IP包头部记录了数据包ID和数据标识,方便以后重新组合。当一个数据包需要通过多个网络传输时,IP层数据包大于MTU时,需要就行IP分片。IP分片缺点是如果IP分片其中一个丢失,需要TCP层重新传输数据。
(3)TCP数据发送、接收过程
用户A发送数据到用户B,用户B接收数据
用户A设备调用send()发送数据 -> 数据从用户态复制到内核 -> 组装tcp协议头 ->放入tcp_write_queue发送队列 -> 调用tcp_push发送数据到IP层 -> ip层组装IP报文头 -> 调用网卡驱动程序将数据发送出去
用户B网卡接收到报文并判断为TCP协议 -> 把数据插入receive队列(这里增加了out_of_order防止数据乱序) -> 拷贝数据从内核态到用户态 -> 应用程序调用
总的来说,tcp交换是以报文段来标记数据的,但是TCP传输的本质是数据流的传递,TCP的实现是通过socket套接字从源设备的发送缓冲区取出MSS大小的数据加上传输头交由下面协议层传输,最终传输到目标设备的接收缓冲区,目标设备数据链路层拿MTU字节数据,再依次去掉传输头向上传输。如果没有拆包操作就可能造成IP分片(IP分片可被禁用)。所以,大数据量传输时需要提前拆包传输
TCP数据包长度 = MTU - IP首部长度(IPv4 20字节,IPv6 40字节 )- TCP包首部长度(20字节 )
二、代码介绍
//拆包
//1400 = mtu1518 - 18 - 20 - 40 = 1440 预留40
//当前代码设置大于1400就开始拆包
byte[] packByte = new byte[1400];
int i = 0;
int pageNum = inData.length; //数据包的大小
init();//初始化函数,如果初始化函数传输到了服务器端,可以设置数据包防止粘包
while (pageNum > 0){//如果数据包大于0进入循环
if (pageNum > 1400){ //大于1400循环调用update发送数据
System.arraycopy(inData, i * 1400, packByte, 0, 1400);
update(packByte, 1400);
pageNum -= 1400;
i++;
}else { //最后,调用一次update函数
byte[] packByte1 = new byte[pageNum];
System.arraycopy(inData, i * 1400, packByte1, 0, pageNum);
update(packByte1, pageNum);
pageNum -= pageNum;
}
}
final();