本文档整理并出自尚硅谷韩顺平Netty教程
https://www.bilibili.com/video/BV1DJ411m7NR
socket
中还有没有读取的内容,也只能放在下一次读取事件中进行。假设客户端同时发送了两个数据包D1和D2给服务端,由于服务端一次读取到字节数是不确定的,固可能存在以下四种情况:
int
(固定四个字节)类型的数据内容长度本实例主要演示出现拆包和粘包的场景。
客户端:
我们将使用循环连续发送10个String
类型的字符串。这里相当于发送了10次。
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//使用客户端发送10条数据,hello,server
for (int i = 0; i < 10; i++) {
String msg = "server" + i + " ";
System.out.println("发送消息 " + msg);
ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
ctx.writeAndFlush(byteBuf);
}
}
服务端:
我们接受客户端发过来的字符串。
private int count = 0;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
//将buffer转成字符串
String message = new String(bytes, CharsetUtil.UTF_8);
System.out.println("服务器接收到数据 " + message);
System.out.println("服务器接收到消息量 = " + (++this.count));
//服务器回送数据到客户端,回送一个随机Id
ByteBuf response = Unpooled.copiedBuffer(UUID.randomUUID().toString() + "--", CharsetUtil.UTF_8);
ctx.writeAndFlush(response);
}
服务端输出结果如下:
自定义Message对象:
public class MessageProtocol {
private int len; //关键
private byte[] content;
}
添加将ByteBuf转换成Message的解码器:
public class MessageDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
System.out.println("MessageDecoder 被调用");
//需要将获取到的二进制字节码转换成 MessageProtocol
int length = in.readInt();
byte[] content = new byte[length];
in.readBytes(content);
//封装成 MessageProtocol 对象,放入 out,传递到下一个Handler
MessageProtocol messageProtocol = new MessageProtocol();
messageProtocol.setLen(length);
messageProtocol.setContent(content);
out.add(messageProtocol);
}
}
添加将Message转换为ByteBuf的编码器:
public class MessageEncoder extends MessageToByteEncoder<MessageProtocol> {
@Override
protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception {
System.out.println("MessageEncoder 方法被调用");
out.writeInt(msg.getLen());
out.writeBytes(msg.getContent());
}
}
客户端连续发送3个Message对象:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//使用客户端发送10条数据,"今天天气冷,吃火锅" 编号
for (int i = 0; i < 3; i++) {
String message = "Server" + i;
byte[] content = message.getBytes(CharsetUtil.UTF_8);
int length = content.length;
//创建协议包对象
MessageProtocol messageProtocol = new MessageProtocol();
messageProtocol.setLen(length);
messageProtocol.setContent(content);
ctx.writeAndFlush(messageProtocol);
}
}
服务端接收:
//接收的Handler继承了SimpleChannelInboundHandler,以MessageProtocol的类型接受消息
@Override
protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception {
//接收到数据,并处理
int len = msg.getLen();
byte[] content = msg.getContent();
System.out.println("服务器第 " + (++count) +" 次接收到信息如下:");
System.out.println("长度:" + len);
System.out.println("内容:" + new String(content, CharsetUtil.UTF_8));
//回复消息
String response = UUID.randomUUID().toString();
int length = response.getBytes(CharsetUtil.UTF_8).length;
MessageProtocol messageProtocol = new MessageProtocol();
messageProtocol.setLen(length);
messageProtocol.setContent(response.getBytes());
ctx.writeAndFlush(messageProtocol);
}
结果展示:
首先,当客户端的通道激活后,就直接调用方法发送10个Message对象。
服务端接收对象时,首先调用MessageDecoder
进行解码,将ByteBuf
类型的数据转换成MessageProtocol
,然后再进入进行读取的Handler
中读取消息。
图片转存中…(img-amPFcFuc-1622360503339)]
首先,当客户端的通道激活后,就直接调用方法发送10个Message对象。
服务端接收对象时,首先调用MessageDecoder
进行解码,将ByteBuf
类型的数据转换成MessageProtocol
,然后再进入进行读取的Handler
中读取消息。
最后返回给客户端消息,调用MessageEncoder
将MessageProtocol
转换成Byte
然后发送出去。