一.实现流程图
二.代码实现
-
整体结构图
-
utils/Attributes
/**
* @Auther: ARong
* @Date: 2020/2/7 9:47 下午
* @Description: channel的参数
*/
public interface Attributes {
AttributeKey<Boolean> LOGIN = AttributeKey.newInstance("login");
}
- utils/Command
/*
* @Author ARong
* @Description 定义指令集
* @Date 2020/2/4 8:58 下午
**/
public interface Command {
byte LOGIN_REQUEST = 1; // 登陆请求指令
byte LOGIN_RESPONSE = 2; // 登陆响应指令
byte MESSAGE_REQUEST = 3; // 普通消息请求指令
byte MESSAGE_RESPONSE = 4; // 普通消息响应指令
}
- utils/MyJsonSerializer
/**
* @Auther: ARong
* @Date: 2020/2/4 9:09 下午
* @Description:
*/
public class MyJsonSerializer implements MySerializer {
/*
* @Author ARong
* @Description 自定义JSON序列化器 将Java对象转化为二进制字节流
* @Date 2020/2/4 9:07 下午
* @Param object
* @return byte[]
**/
@Override
public byte[] serialize(Object object) {
return JSON.toJSONBytes(object);
}
/*
* @Author ARong
* @Description 二进制转换成 java 对象
* @Date 2020/2/4 9:08 下午
* @Param [clazz, bytes]
* @return T
**/
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
return JSON.parseObject(bytes, clazz);
}
}
- utils/MySerializer
/*
* @Author ARong
* @Description 定义序列化器
* @Date 2020/2/4 9:06 下午
**/
public interface MySerializer {
/*
* @Author ARong
* @Description 将Java对象转化为二进制字节流
* @Date 2020/2/4 9:07 下午
* @Param object
* @return byte[]
**/
byte[] serialize(Object object);
/*
* @Author ARong
* @Description 二进制转换成 java 对象
* @Date 2020/2/4 9:08 下午
* @Param [clazz, bytes]
* @return T
**/
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
- utils/PacketUtil
/**
* @Auther: ARong
* @Date: 2020/2/4 9:14 下午
* @Description: 用于包编码和解码
*/
public class PacketUtil {
/*
* @Author ARong
* @Description 将Packet按照约定序列化方式编码成ByteBuf
* @Date 2020/2/4 9:15 下午
* @Param [packet]
* @return io.netty.buffer.ByteBuf
**/
public static ByteBuf encode(Packet packet) {
// 创建ByteBuf对象
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
// 获取序列化器序列化对象
MySerializer serializer = SerializerFactory.getSerializer(packet.getSerMethod());
byte[] data = serializer.serialize(packet);
// 按照通信协议填充ByteBUf
byteBuf.writeInt(packet.getMagic());// 魔数
byteBuf.writeByte(packet.getVersion()); // 版本号
byteBuf.writeByte(packet.getSerMethod()); // 序列化方式
byteBuf.writeByte(packet.getCommand()); // 指令
byteBuf.writeInt(data.length);// 数据长度
byteBuf.writeBytes(data); // 数据
return byteBuf;
}
/*
* @Author ARong
* @Description 将ByteBuf按照约定序列化方式解码成Packet
* @Date 2020/2/4 9:21 下午
* @Param [byteBuf]
* @return io_learn.netty.part4_protocol.packet.Packet
**/
public static Packet decode(ByteBuf byteBuf) {
// 暂不判断魔数,跳过
byteBuf.skipBytes(4);
// 暂不判断魔数版本号,跳过
byteBuf.skipBytes(1);
// 获取序列化方式
byte serMethod = byteBuf.readByte();
// 获取指令
byte command = byteBuf.readByte();
// 获取数据包长度
int length = byteBuf.readInt();
// 获取存储数据的字节数组
byte[] data = new byte[length];
byteBuf.readBytes(data);
// 反序列化数据,获取Packet
Class<? extends Packet> packetType = getPacketType(command);
Packet res = SerializerFactory.getSerializer(serMethod).deserialize(packetType, data);
return res;
}
/*
* @Author ARong
* @Description 通过指令获取相应的Packet
* @Date 2020/2/4 9:31 下午
* @Param [commond]
* @return io_learn.netty.part4_protocol.packet.Packet
**/
public static Class<? extends Packet> getPacketType(byte commond) {
if (commond == Command.LOGIN_REQUEST) {
return LoginRequestPacket.class;
}
if (commond == Command.LOGIN_RESPONSE) {
return LoginResponsePacket.class;
}
if (commond == Command.MESSAGE_REQUEST) {
return MessageRequestPacket.class;
}
if (commond == Command.MESSAGE_RESPONSE) {
return MessageResponsePacket.class;
}
return null;
}
}
- utils/Serialize
/*
* @Author ARong
* @Description 定义序列化方式
* @Date 2020/2/4 9:05 下午
**/
public interface Serialize {
byte JSON = 1;
}
- utils/SerializerFactory
/*
* @Author ARong
* @Description 序列化器工厂
* @Date 2020/2/4 8:56 下午
**/
public class SerializerFactory {
/*
* @Author ARong
* @Description 通过序列化名称获取相应的序列化器
* @Date 2020/2/4 9:17 下午
* @Param [serName]
* @return io_learn.netty.part4_protocol.utils.MySerializer
**/
public static MySerializer getSerializer(byte serName) {
if (serName == Serialize.JSON) {
return new MyJsonSerializer();
}
return null;
}
}
- packet/Packet
/**
* @Auther: ARong
* @Date: 2020/2/4 8:44 下午
* @Description: 自定义通信协议的抽象包
*/
@Data
public abstract class Packet {
private int magic; // 魔数
private byte version; // 版本号
private byte serMethod; // 的序列化/反序列化方式
private byte command; // 指令
}
- packet/MessageResponsePacket
/**
* @Auther: ARong
* @Date: 2020/2/4 8:52 下午
* @Description: 用以数据请求响应的协议包
*/
@Data
public class MessageResponsePacket extends Packet {
private int magic = 20202020; // 魔数
private byte version = 1; // 版本号
private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
private byte command = Command.MESSAGE_RESPONSE; // 登陆响应指令
// ---------以下为返回的响应数据----------
private String message;
@Override
public String toString() {
return "MessageRequestPacket{" +
"magic=" + magic +
", version=" + version +
", serMethod=" + serMethod +
", command=" + command +
", message='" + message + '\'' +
'}';
}
}
- packet/MessageRequestPacket
/**
* @Auther: ARong
* @Date: 2020/2/4 8:52 下午
* @Description: 用以数据请求的协议包
*/
@Data
public class MessageRequestPacket extends Packet {
private int magic = 20202020; // 魔数
private byte version = 1; // 版本号
private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
private byte command = Command.MESSAGE_REQUEST; // 登陆响应指令
// ---------以下为请求数据----------
private String message;
@Override
public String toString() {
return "MessageRequestPacket{" +
"magic=" + magic +
", version=" + version +
", serMethod=" + serMethod +
", command=" + command +
", message='" + message + '\'' +
'}';
}
}
- packet/LoginResponsePacket
/**
* @Auther: ARong
* @Date: 2020/2/4 8:52 下午
* @Description: 用以登陆响应协议包
*/
@Data
public class LoginResponsePacket extends Packet {
private int magic = 20202020; // 魔数
private byte version = 1; // 版本号
private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
private byte command = Command.LOGIN_RESPONSE; // 登陆响应指令
// ---------以下为登陆响应数据----------
private boolean status;
private String message;
@Override
public String toString() {
return "LoginResponsePacket{" +
"magic=" + magic +
", version=" + version +
", serMethod=" + serMethod +
", command=" + command +
", loginStatus=" + status +
", message='" + message + '\'' +
'}';
}
}
- packet/LoginRequestPacket
/**
* @Auther: ARong
* @Date: 2020/2/4 8:52 下午
* @Description: 用以登陆的登陆协议包
*/
@Data
public class LoginRequestPacket extends Packet {
private int magic = 20202020; // 魔数
private byte version = 1; // 版本号
private byte serMethod = Serialize.JSON; // 序列化反序列化方式为Json
private byte command = Command.LOGIN_REQUEST; // 登陆指令
// ---------以下为登陆数据----------
private long userId;
private String name;
private String password;
@Override
public String toString() {
return "LoginPacket{" +
"magic=" + magic +
", version=" + version +
", serMethod=" + serMethod +
", command=" + command +
", userId=" + userId +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
- handler/MyServerHandler
public class MyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 解码
ByteBuf response = ByteBufAllocator.DEFAULT.ioBuffer();
Packet packet = PacketUtil.decode((ByteBuf) msg);
if (packet instanceof LoginRequestPacket) {
System.out.println("=====服务端接收到客户端登陆请求,正在校验=====");
LoginRequestPacket request = (LoginRequestPacket) packet;
LoginResponsePacket responsePacket = new LoginResponsePacket();
if (check(request)) {
System.out.println("登陆成功:"+request);
// 为channel设置登陆成功属性,下次请求时进行校验
ctx.channel().attr(Attributes.LOGIN).set(true);
responsePacket.setStatus(true);
} else {
System.out.println("登陆失败:"+request);
responsePacket.setStatus(false);
}
// 编码登陆响应返回
response = PacketUtil.encode(responsePacket);
}
if (packet instanceof MessageRequestPacket) {
// 首先获取登陆状态
if (ctx.channel().attr(Attributes.LOGIN).get() == null) {
System.out.println("=====客户端尚未登陆,无法进行数据通信=====");
} else {
System.out.println("=====客户端登陆成功,进行数据通信=====");
MessageRequestPacket request = (MessageRequestPacket) packet;
System.out.println("收到:"+request.toString());
// 编码
MessageResponsePacket responsePacket = new MessageResponsePacket();
responsePacket.setMessage("数据通信响应测试Reponse Test1");
response = PacketUtil.encode(responsePacket);
}
}
ctx.writeAndFlush(response);
}
private boolean check(LoginRequestPacket request) {
// 硬编码校验条件
if ("ARong".equals(request.getName()) && "2020".equals(request.getPassword())) {
return true;
}
return false;
}
}
- hanlder/MyClientHandler
/**
* @Auther: ARong
* @Date: 2020/2/5 1:17 下午
* @Description: 客户端的Handler
*/
public class MyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 第一次连接,首先尝试登陆
System.out.println("=====客户端连接成功,尝试登陆=====");
// 收集需要编码的登陆消息
Scanner input = new Scanner(System.in);
LoginRequestPacket packet = new LoginRequestPacket();
System.out.println("请输入登陆的用户ID");
packet.setUserId(Long.parseLong(input.nextLine()));
System.out.println("请输入登陆的用户账户");
packet.setName(input.nextLine());
System.out.println("请输入登陆的用户密码");
packet.setPassword(input.nextLine());
System.out.println("=====客户端开始发送登陆数据包=====");
// 编码数据包进行发送
ByteBuf request = PacketUtil.encode(packet);
ctx.writeAndFlush(request);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 解码
Packet packet = PacketUtil.decode((ByteBuf) msg);
if (packet instanceof LoginResponsePacket) {
LoginResponsePacket response = (LoginResponsePacket) packet;
if (response.isStatus()) {
System.out.println("=====客户端登陆成功=====");
} else {
System.out.println("=====客户端登陆失败=====");
}
// 编码
MessageRequestPacket request = new MessageRequestPacket();
request.setMessage("数据通信请求测试Request Test1");
ByteBuf byteBuf = PacketUtil.encode(request);
ctx.writeAndFlush(byteBuf);
}
if (packet instanceof MessageResponsePacket) {
MessageResponsePacket request = (MessageResponsePacket) packet;
System.out.println("收到:"+request.toString());
}
}
}
- server/NettyServer
/**
* @Auther: ARong
* @Date: 2020/2/4 9:13 下午
* @Description: Netty Server
*/
public class NettyServer {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(
new ChannelInitializer<SocketChannel>() {
@Override
// 初始化channel
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyServerHandler());
}
}
).bind(8000);
}
}
- server/NettyClient
/**
* @Auther: ARong
* @Date: 2020/2/5 1:28 下午
* @Description:
*/
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyClientHandler());
}
});
// 连接服务端
bootstrap.connect("127.0.0.1", 8000);
}
}