文章目录
- 1、WebSocket链接建立
- 2、实现用户上线功能
- 3、私聊发送消息
注意:
该文章不会详细介绍Netty相关概念和原理,主要目的是介绍如何快速构建聊天室Demo
不会在文章主体过多说明代码流程,文章中的代码已经配备了详细的注释
1、WebSocket链接建立
首先创建maven项目,引入相关依赖:
<dependencies>
<!--Netty-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.85.Final</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.18</version>
</dependency>
</dependencies>
编写服务端代码,用于监听客户端发送的事件:
/**
* @Author 徐志斌
* @Date: 2023/1/17 20:06
* @FileName: IMServer
* @Description: 通过WebSocket进行通信,不使用HTTP进行数据通信
*/
public class IMServer {
/**
* 启动服务端
*/
public static void start() {
// 1.创建两个EventLoop组,用于处理不同的事件
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
// 2.服务端处理流程
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class) // 与客户端进行读写的数据通道channel
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception { // 连接建立后会调用该方法初始化
// pipeline流水线:管理服务端整体的处理流程
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpServerCodec()) // HTTP编码器
.addLast(new ChunkedWriteHandler()) // 支持大数据流
.addLast(new HttpObjectAggregator(1024 * 64)) // 对HTTP消息进行聚合
.addLast(new WebSocketServerProtocolHandler("/")) // WebSocket协议
.addLast(new WebSocketHandler()); // 自定义处理器
}
});
// 3.监听8080端口
ChannelFuture future = bootstrap.bind(8080);
}
}
编写自定义处理器代码,将接收到的通信信息打印到控制台:
/**
* @Author 徐志斌
* @Date: 2023/1/17 20:16
* @FileName: WebSocketHandler
* @Description:
*/
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame frame) throws Exception {
System.out.println(frame.text());
}
}
最后,使用Main方法调用IMServer.start()
启动,监听8080端口,等待客户端发送数据
本次案例不通过HTTP协议进行通信,使用WebSocket协议,可以通过浏览器访问:http://websocket-test.com/,通过该网页为我们的服务端发送数据,效果如下,说明连接建立成功!
控制台信息:
2、实现用户上线功能
在IMServer中,加入ConcurrentHashMap,用于存放上线后的用户信息:
// 与服务端连接成功的用户存放到该Map中
public static final Map<String, Channel> USERS = new ConcurrentHashMap<>();
调整自定义事件中的代码,监听连接事件:
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
try {
// 解析客户端发送的消息,类中数据根据实际事件信息进行封装
Command command = JSON.parseObject(frame.text(), Command.class);
// 判断事件类型,不同事件类型不同处理方式
switch (command.getCode()) {
case "连接事件":
ConnectionHandler.execute(ctx, command); // 客户端发送的是建立连接事件
default:
ctx.channel().writeAndFlush("不支持该事件");
}
} catch (Exception e) {
ctx.channel().writeAndFlush(e);
}
}
}
连接事件处理类:
public class ConnectionHandler {
/**
* 建立连接
*/
public static void execute(ChannelHandlerContext ctx, Command command) {
// 如果已经连接过,就不能进行连接了
if (IMServer.USERS.containsKey(command.getNickName())) {
ctx.channel().writeAndFlush("该用户已上线");
ctx.channel().disconnect();
return;
}
// 连接后,将用户信息存入IMServer的Map中
IMServer.USERS.put(command.getNickName(), ctx.channel());
// 提示客户端,连接成功
ctx.channel().writeAndFlush("系统消息:与服务端建立连接成功");
}
}
3、私聊发送消息
调整自定义事件中的代码,让其支持发送消息事件:
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
try {
// 解析客户端发送的消息,类中数据根据实际事件信息进行封装
Command command = JSON.parseObject(frame.text(), Command.class);
// 判断事件类型,不同事件类型不同处理方式
switch (command.getCode()) {
case "连接事件":
ConnectionHandler.execute(ctx, command); // 客户端发送的是建立连接事件
case "私发信息":
ChatHandler.execute(ctx,frame); // 客户端发送的是发现消息事件
default:
ctx.channel().writeAndFlush("不支持该事件");
}
} catch (Exception e) {
ctx.channel().writeAndFlush(e);
}
}
}
编写发送消息事件处理类代码:
public class ChatHandler {
public static void execute(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
try {
// 将消息格式转成规定好的(不同场景,格式不同)
ChatMessage chat = JSON.parseObject(frame.text(), ChatMessage.class);
// 获取消息接受方
Channel channel = IMServer.USERS.get(chat.getTarget());
// 消息发送给接收方
channel.writeAndFlush(chat.getContent());
} catch (Exception e) {
ctx.channel().writeAndFlush("发送消息格式错误,请确认后尝试");
}
}
}