1、自定义通讯协议父类
package com.example.demo.im;
import lombok.Data;
@Data
//自定义通讯协议
// 魔数(4字节)+版本号(1字节)+序列化算法(1字节)+指令(1字节)+数据长度(4字节)+数据(N字节)
public abstract class Packet {
/**
* 协议版本
*/
private Byte version = 1;
/**
* 指令
* @return
*/
public abstract Byte getCmd();
}
LengthFieldBasedFrameDecoder 编解码
package com.example.demo.im;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
public class Spliter extends LengthFieldBasedFrameDecoder {
private static final int LENGTH_FIELD_OFFSET = 7;
private static final int LENGTH_FIELD_LENGTH = 4;
public Spliter() {
super(Integer.MAX_VALUE, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
return super.decode(ctx, in);
}
}
2、消息类型定义
package com.example.demo.im;
public interface Cmd {
//登录请求
Byte LOGIN_REQUEST = 1;
// 登录响应
Byte LOGIN_RESPONSE = 2;
// 发送消息请求
Byte MESSAGE_REQUEST = 3;
// 发送消息响应
Byte MESSAGE_RESPONSE = 4;
//心跳请求
Byte HEARTBEAT_REQUEST = 17;
//心跳响应
Byte HEARTBEAT_RESPONSE = 18;
}
3、java消息类序列化接口
package com.example.demo.im;
public interface SelfSerializable {
SelfSerializable DEFAULT = new JsonSelfSerializable();
/**
* 序列化算法
*
* @return
*/
byte getSerializableAlgorithm();
/**
* java对象转二进制数据
*
* @param object
* @return
*/
byte[] serialize(Object object);
/**
* 二进制转为java对象
*
* @param clzz
* @param bytes
* @param <T>
* @return
*/
<T> T deserialize(byte[] bytes, Class<T> clzz);
}
对应的序列化,本例只实现了阿里的fastjson序列化
package com.example.demo.im;
public interface SerializableAlgorithm {
/**
* 阿里json序列化
*/
byte ALIJSON = 1;
}
阿里fastjson序列化和反序列化实现
package com.example.demo.im;
import com.alibaba.fastjson.JSON;
public class JsonSelfSerializable implements SelfSerializable {
@Override
public byte getSerializableAlgorithm() {
return SerializableAlgorithm.ALIJSON;
}
@Override
public byte[] serialize(Object object) {
return JSON.toJSONBytes(object);
}
@Override
public <T> T deserialize( byte[] bytes,Class<T> clzz) {
return JSON.parseObject(bytes,clzz);
}
}
登录请求消息
package com.example.demo.im;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
//登录请求数据包
public class LoginRequestPacket extends Packet {
private String userId;
private String userName;
private String password;
@Override
public Byte getCmd() {
return Cmd.LOGIN_REQUEST;
}
}
登录响应消息
package com.example.demo.im;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginResponsePacket extends Packet {
private String userId;
private String userName;
private String success;
private String reason;
@Override
public Byte getCmd() {
return Cmd.LOGIN_RESPONSE;
}
}
心跳请求消息
package com.example.demo.im;
public class HeartBeatRequestPacket extends Packet {
/**
* 指令
*
* @return
*/
@Override
public Byte getCmd() {
return Cmd.HEARTBEAT_REQUEST;
}
}
心跳响应消息
package com.example.demo.im;
public class HeartBeatResponsePacket extends Packet {
@Override
public Byte getCmd() {
return Cmd.HEARTBEAT_RESPONSE;
}
}
单聊发送请求消息
package com.example.demo.im;
import lombok.Data;
@Data
//客户端发送至服务端的消息对象
public class MessageRequestPacket extends Packet{
private String toUserId;
private String msg;
private String fromUserId;
@Override
public Byte getCmd() {
return Cmd.MESSAGE_REQUEST;
}
}
单聊接收响应消息
package com.example.demo.im;
import lombok.Data;
@Data
//服务端发送至客户端的消息
public class MessageResponsePacket extends Packet {
private String fromUserId;
private String fromUserName;
private String msg;
@Override
public Byte getCmd() {
return Cmd.MESSAGE_RESPONSE;
}
}
消息编解码
package com.example.demo.im;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List;
@ChannelHandler.Sharable
public class PacketCodecHandler extends MessageToMessageCodec<ByteBuf, Packet> {
public static final PacketCodecHandler INSTANCE = new PacketCodecHandler();
private PacketCodecHandler() {
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) {
out.add(PacketCodec.INSTANCE.decode(byteBuf));
}
@Override
protected void encode(ChannelHandlerContext ctx, Packet packet, List<Object> out) {
ByteBuf byteBuf = ctx.c