当前位置: 首页 > 后端技术 > Java

粘包和报文不全的问题

时间:2023-04-01 22:50:40 Java

粘包和报文不全的问题粘包和报文不全的问题其实是应用层的问题,和TCP无关。TCP可以保证顺序和完备性这篇文章只是对什么是消息粘包和消息不完备性做一个简单的解释。1.重现消息粘包和消息不完整,在之前通过NIO改造聊天室的案例中,我们复现了消息粘包和不完整的消息1.1消息粘包问题的复现。一次发送100个publicstaticvoidconnectServer(ServerInfoserverInfo){try{//打开tcp连接socket=newSocket(Inet4Address.getByName(serverInfo.getIp()),serverInfo.getPort());//开启线程异步读取服务器消息clientReadHandler=newClientReadHandler(socket.getInputStream(),ClientConnectTcp::close);clientReadHandler.start();//监听键盘写入system.insystemInReader=newBufferedReader(newInputStreamReader(System.in));clientWriteHandler=newClientWriteHandler(socket.getOutputStream());做{字符串消息=systemInReader.readLine();//异步发送到服务器if(message!=null){//将读取的消息发送100次并为其添加i标志for(inti=0;i<100;i++){clientWriteHandler.sendMsg(message+“:”+我);}}if(CommonConstants.BYE_FLAG.equals(message)){close();}}while(!doReadFlag);}catch(IOExceptione){log.error("[建立tcp连接异常:{}]",e.getMessage());}最后{关闭();}}服务端收到的消息可以看出存在严重的粘包问题。本来我们希望消息一条一条处理,如下:receiveAsyncmessage:abcdefg:0receiveAsyncmessage:abcdefg:1receiveAsyncmessage:abcdefg:210:12:21.721[read-io-executors1]INFOcom.johnny.chatroom...efg:15abcdefg:16abcdefg:17abcdefg:18abcdefg:19abcdefg:20abcdefg:21abcdefg:22abcdefg:23a10:12:21.722[read-io-executors2]INFOcom.johnny.chatroom.lib.nio.Connector-接收:4cdef消息:g:225abcdefg:261.2Recurrencemessageincompleteproblem修改服务器端IoArgs的ByteBuffer的缓冲区大小@Slf4j@DatapublicclassIoArgs{//将缓冲区大小从256字节改为4字节privateByteBufferbyteBuffer=ByteBuffer.allocate(4);其他代码略去...}Client依然发送100条数据如下do{Stringmessage=systemInReader.readLine();//异步发送到服务器if(message!=null){for(inti=0;i<100;i++){clientWriteHandler.sendMsg(message+":"+i);}}if(CommonConstants.BYE_FLAG.equals(message)){close();}}while(!doReadFlag);Server端从收到的消息中可以看出,原来的消息abcdefg已经被拆解成很多子消息。出现了严重的消息不完整问题10:19:38.754[read-io-executors1]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:abc10:19:38.754[read-io-executors2]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:efg10:19:38.755[read-io-executors3]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:0a10:19:38.755[read-io-executors4]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:cde10:19:38.756[read-io-executors1]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:g:110:19:38.756[read-io-executors2]INFOcom.johnny.chatroom.lib.nio.Connector-接收异步消息:abc10:19:38.756[read-io-executors3]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsync消息:efg10:19:38.756[read-io-executors4]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:2a10:19:38.756[read-io-executors1]INFOcom.johnny.chatroom.lib.nio.Connector-接收异步消息:cde10:19:38.757[阅读-io-executors2]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsync消息:g:310:19:38.757[read-io-executors3]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsync消息:abc10:19:38.757[read-io-executors4]INFOcom.johnny.chatroom.lib.nio.Connector-接收异步消息:efg10:19:38.757[read-io-executors1]INFOcom.johnny.chatroom.lib.nio.Connector-receiveAsyncmessage:42.消息粘包和不完整消息概述在socket网络编程中,都是端到端的通信,由clientport+serverport+clientIP+serverIP+transmissionprotocol组成。一个元组可以清楚地标识一个连接。在TCP套接字编程中,发送方和接收方都有成对的套接字。为了更高效地向接收端发送多个数据包,发送端采用了一种优化算法(Nagle算法),将间隔较小、数据量较小的数据组合成一个数据量大的数据块,然后进行打包。这样,接收端就必须使用高效、科学的拆包机制来区分这些数据。2.1什么是TCP粘包问题?TCP粘包是指发送方发送的几包数据在到达接收方时被粘在一个数据包中。从接收缓冲区的角度来看,下一个数据包的头部跟在上一个数据包的尾部之后。粘包的原因有很多,可能来自发送方,也可能来自接收方。2.2TCP粘包产生的原因发送方的原因是TCP默认使用了Nagle算法(主要作用:减少网络中的段数),Nagle算法主要做了两件事:只有当上一个包被确认时,是否会发送下一个数据包Packets收集多个小数据包,并在确认到达时将它们一起发送。Nagle算法可能会导致发送方出现粘包。接收方的原因是TCP收到一个数据包后,不会立即交给应用层处理,或者说应用层没有立即处理。实际上,TCP将接收到的数据包保存在接收缓冲区中,然后应用程序主动从缓冲区中读取接收到的数据包。这样,如果TCP将数据包接收到缓存中的速度快于应用程序从缓存中读取数据包的速度,则会缓存多个数据包,应用程序可能会读取多个端到端连接.包裹。什么时候需要处理粘包现象?如果发送方发送的多组数据本来就是同一份数据的不同部分,比如一个文件被分成多个部分发送,那么当然就不需要处理粘包了。如果多个组是不相关的,甚至是并行的,那么这个时候,我们就要处理粘包现象了。2.3如何处理粘包现象?发送端可以通过关闭Nagle算法来解决发送端引起的粘包问题,使用TCP_NODELAY选项关闭该算法。接收方对于粘包现象没有办法处理,只能将问题交给应用层处理。应用层应用层的解决方案简单可行。既可以解决接收方的粘包问题,也可以解决发送方的粘包问题。1.固定包长的数据包2.指定字符(字符串)作为包的结束标志,如换行符\n3.包头+包体格式这种格式的包一般分为两部分,即包头和包体,包头是固定大小的,包头必须包含一个字段来表示后面的包体有多大.小结本文简单介绍一下什么是消息粘包和消息不完整的问题,并通过代码复现出现过的问题。那我们后面会写关于粘包等问题的具体处理。核心思想是通过读取包头来获取消息。读取数据包的长度,然后根据长度读取后面的数据。如果不够就缓存起来等待下一个数据包。如果长度够了,就丢给上层处理。既能解决消息粘包问题,又能解决消息不完整问题。问题,具体的代码演示会在下一篇文章中讨论。欢迎大家访问个人博客JohnnyHut