一、前置说明
本文实现思路和代码参考:
二、Netty主要知识点
2.1 ChannelHandler生命周期
2.2 Handler、Option、Attr
序号 | boss线程组 | worker线程组 |
---|---|---|
01 | handler | childHandler |
02 | option | childOption |
03 | attr | childAttr |
ServerBootStrap.option参数适用于正在侦听连接的服务器套接字(服务器通道),ServerBootStrap.childOption参数适用于在服务器套接字接受连接后创建的套接字。
2.3 writeAndFlush方法
ctx(ChannelHandlerContext)的writeAndFlush是从当前handler直接发出这个消息,而channel的writeAndFlush是从整个pipline最后一个outhandler发出。
2.4 粘包/拆包(半包)
粘包/拆包图示如下所示:
接收端收到的第一个包,正常。
接收端收到的第二个包,就是一个粘包。 将发送端的第二个包、第三个包,粘在一起了。
接收端收到的第三个包,第四个包,就是半包。将发送端的的第四个包,分开成了两个了。
三、客户端单聊实现思路
指令列表如下所示:
序号 | 客户端 | 服务端 |
---|---|---|
登录请求 | 发送 | 接收 |
登录响应 | 接收 | 发送 |
客户端发消息 | 发送 | 接收 |
服务端发消息 | 接收 | 发送 |
登出请求 | 发送 | 接收 |
登出响应 | 接收 | 发送 |
四、客户端单聊实现
4.1 通信协议设计(ByteBuf)
魔数 | 版本号 | 序列化算法 | 指令 | 数据长度 | 数据 |
4Byte | 1Byte | 1Byte | 1Byte | 4Byte | 4Byte |
4.2 部分代码讲解
详细代码请参考:
//指定魔数
public static final int MAGIC_NUMBER = 0x12345678;
//指令-数据Map
private static final Map<Byte, Class<? extends Packet>> packetTypeMap;
//序列化算法Map
private static final Map<Byte, Serializer> serializerMap;
static {
packetTypeMap = new HashMap<>();
packetTypeMap.put(Command.LOGIN_REQUEST, LoginRequestPacket.class);
packetTypeMap.put(Command.LOGIN_RESPONSE, LoginResponsePacket.class);
packetTypeMap.put(Command.MESSAGE_REQUEST, MessageRequestPacket.class);
packetTypeMap.put(Command.MESSAGE_RESPONSE, MessageResponsePacket.class);
serializerMap = new HashMap<>();
Serializer serializer = new JSONSerializer();
serializerMap.put(serializer.getSerializerMethod(), serializer);
}
/**
* userID ---- Channel 对应
*/
private static final Map<String,Channel> userIdChannelMap = new ConcurrentHashMap<>();
public static void bindSession(Session session,Channel channel){
userIdChannelMap.put(session.getUserId(),channel);
//设置channel attr属性
channel.attr(ProtocolAttributes.SESSION).set(session);
}
public static void unBindSession(Channel channel){
if (hasLogin(channel)){
userIdChannelMap.remove(getSession(channel).getUserId());
channel.attr(ProtocolAttributes.SESSION).set(null);
}
}
五、功能测试
先启动NettyServer服务端,再启动NettyClient和NettyClient2客户端。客户端输入用户名返回服务端生成的用户ID。
客户端NettyClient输入用户world用户ID,空格后输入消息内容。