生产者向服务端发送请求之后,服务端会向生产者发送一个响应,这时就会遇到粘包和拆包的问题,本节我们来看一下如何处理粘包问题
为什么会有粘包问题
一个请求里面会带有多个响应
多个消息粘在一起发送回来了,正常应该是一条一条发送回来的
我们接着上节的代码看
//核心代码,处理发送请求的事件
//selector注册了一个OP_WRITE事件
//selector还注册了一个OP_READ事件
if (channel.ready() && key.isWritable()) {
//这里就是要往服务端发送数据了
Send send = channel.write();
if (send != null) {
this.completedSends.add(send);
this.sensors.recordBytesSent(channel.id(), send.size());
}
}
这里由于selector还注册了一个OP_READ事件,所以下一次循环进来的时候,会走到下面的分支
/* if channel is ready read from any connections that have readable data */
if (channel.ready() && key.isReadable() && !hasStagedReceive(channel)) {
NetworkReceive networkReceive;
//接收服务端发送回来的响应
while ((networkReceive = channel.read()) != null)
addToStagedReceives(channel, networkReceive);
}
可以先来看一下如何读取这些响应
public NetworkReceive read() throws IOException {
NetworkReceive result = null;
if (receive == null) {
receive = new NetworkReceive(maxReceiveSize, id);
}
//接收响应
receive(receive);
if (receive.complete()) {
receive.payload().rewind();
result = receive;
receive = null;
}
return result;
}
private long receive(NetworkReceive receive) throws IOException {
//读取数据
return receive.readFrom(transportLayer);
}
接下来就要到核心代码了
这里我们先来了解一下解决粘包问题的解决方案
int类型的数(消息大小)消息 int类型的数 消息
主要就是用这样的一个格式来解决
@Deprecated
public long readFromReadableChannel(ReadableByteChannel channel) throws IOException {
int read = 0;
if (size.hasRemaining()) {
//先读取4字节的数据(代表的就是后面跟着的消息体的大小)
int bytesRead = channel.read(size);
if (bytesRead < 0)
throw new EOFException();
read += bytesRead;
if (!size.hasRemaining()) {
size.rewind();
int receiveSize = size.getInt();
if (receiveSize < 0)
throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + ")");
if (maxSize != UNLIMITED && receiveSize > maxSize)
throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + " larger than " + maxSize + ")");
//分配一个内存空间,这个内存空间的大小
//就是刚刚读出来的那个4字节的int的大小
this.buffer = ByteBuffer.allocate(receiveSize);
}
}
if (buffer != null) {
//读取数据
int bytesRead = channel.read(buffer);
if (bytesRead < 0)
throw new EOFException();
read += bytesRead;
}
return read;
}
public NetworkReceive read() throws IOException {
NetworkReceive result = null;
if (receive == null) {
receive = new NetworkReceive(maxReceiveSize, id);
}
//接收响应
receive(receive);
//是否读完一个完整的消息
if (receive.complete()) {
receive.payload().rewind();
result = receive;
receive = null;
}
return result;
}
public boolean complete() {
//size没有剩余空间了 && buffer没有剩余空间了
return !size.hasRemaining() && !buffer.hasRemaining();
}