2. Netty+SpringBoot实现IM服务 之 16进制数据及解决半包和粘包

系列文章目录

  1. 技术选型、简单实现
  2. 16进制数据及解决半包和粘包
  3. 拆包器与心跳检测
  4. 用户与channel绑定


制定协议

协议分为head和body,其中head固定是4位,第一位暂时不代表任何意义,后期可以用来表示协议版本和是否压缩等。第二位表示要进行的动作,第三和第四位表示协议body的长度。

在这里插入图片描述

一、协议实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author Mr.Guo
 * @date 2021/3/6 下午12:24
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Xa87Packet implements Serializable, Cloneable {
    private int command;
    private int length;
    private byte[] body;
}

二、创建新的Encoder、Decoder

1.MessageEncoder

import cn.xa87.im.demo.packet.Xa87Packet;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * @author Mr.Guo
 * @date 2021/3/6 上午9:58
 */
public class MessageEncoder extends MessageToByteEncoder<Xa87Packet> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Xa87Packet xa87Packet, ByteBuf byteBuf) throws Exception {
        byteBuf.writeByte(0x00);
        byteBuf.writeByte(0x01);
        byteBuf.writeShort(xa87Packet.getLength());
        byteBuf.writeBytes(xa87Packet.getBody());
    }
}

2.MessageDecoder

import cn.xa87.im.demo.packet.Xa87Packet;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 * @author Mr.Guo
 * @date 2021/3/6 上午9:58
 */
public class MessageDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        int readableLength = byteBuf.readableBytes();
        if (readableLength < 1) {
            return;
        }
        byteBuf.markReaderIndex();
        byte firstByte = byteBuf.readByte();
        byte actByte = byteBuf.readByte();
        int bodyLength = byteBuf.readShort();

        if (readableLength < bodyLength + 4) {
            byteBuf.resetReaderIndex();
            return;
        }

        byte[] body = new byte[bodyLength];
        byteBuf.readBytes(body);

        list.add(new Xa87Packet(actByte, bodyLength, body));
    }
}

三、替换StringEncoder、StringDecoder

import cn.xa87.im.demo.coder.MessageDecoder;
import cn.xa87.im.demo.coder.MessageEncoder;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * @author Mr.Guo
 * @date 2021/3/4 下午4:53
 */
@Component
public class SocketServerInitializer extends ChannelInitializer<SocketChannel> {
    @Autowired
    private SocketMsgHandler socketMsgHandler;

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        //添加对于读写大数据流的支持
        pipeline.addLast(new ChunkedWriteHandler());
        //对httpMessage进行聚合
        pipeline.addLast(new HttpObjectAggregator(1024 * 64));
        //pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        //pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast("decoder", new MessageDecoder());
        pipeline.addLast("encoder", new MessageEncoder());
        //自定义handler
        pipeline.addLast(socketMsgHandler);
    }
}

运行结果

在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
首先,我们需要在Spring Boot项目中引入Netty的依赖: ``` <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.22.Final</version> </dependency> ``` 然后,我们需要编写一个Netty客户端类,用于连接服务器并发送16数据: ```java public class NettyClient { private final String host; private final int port; private Channel channel; public NettyClient(String host, int port) { this.host = host; this.port = port; } public void connect() throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HexEncoder()); pipeline.addLast(new NettyClientHandler()); } }); ChannelFuture future = bootstrap.connect(host, port).sync(); channel = future.channel(); channel.closeFuture().sync(); } finally { group.shutdownGracefully(); } } public void sendHexData(String hexData) { if (channel == null || !channel.isActive()) { throw new IllegalStateException("Connection is not active"); } channel.writeAndFlush(Unpooled.copiedBuffer(hexData, CharsetUtil.US_ASCII)); } } ``` 在上面的代码中,我们使用了HexEncoder类来将字符串转换为16数据,然后将它们发送到服务器。我们还使用了NettyClientHandler类来处理从服务器接收到的响应。 最后,我们可以在Spring Boot应用程序中使用这个Netty客户端类来发送16数据: ```java @RestController @RequestMapping("/api") public class NettyController { @GetMapping("/sendHexData") public String sendHexData(@RequestParam String hexData) throws Exception { NettyClient client = new NettyClient("localhost", 8080); client.connect(); client.sendHexData(hexData); return "OK"; } } ``` 在上面的代码中,我们可以使用sendHexData方法来发送16数据服务器。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值