开发服务端分发消息功能,实现群聊
目录
对Message对象进行细分,拆分成以下对象
TextMessage (数据为文本内容)
BinaryMessage(数据为二进制)
StreamMessage(数据为流数据)
PingMessage(ping消息,对应必须响应pong)
PongMessage(pong消息)
编写CsServerMessageHandler,管理客户端与分发消息
package com.lhstack.example.handler;
import com.lhstack.bio.channel.Channel;
import com.lhstack.bio.channel.ChannelHandlerContext;
import com.lhstack.bio.channel.SimpleChannelAdapterHandler;
import com.lhstack.bio.codec.bs.TextMessage;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author lhstack
* 服务端
*/
public class CsServerMessageHandler extends SimpleChannelAdapterHandler<TextMessage> {
private static final ConcurrentHashMap<String, Channel> MESSAGE_GROUP = new ConcurrentHashMap<>();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MESSAGE_GROUP.forEach((k,v) ->{
try {
v.writeAndFlush(new TextMessage(String.format("%s: 欢迎客户端[%s],加入消息组",getCurrentFormatTime(),ctx.channel().id())));
} catch (Exception e) {
e.printStackTrace();
}
});
ctx.writeAndFlush(new TextMessage(String.format("%s: 你好,欢迎加入消息组",getCurrentFormatTime())));
MESSAGE_GROUP.put(ctx.channel().id(),ctx.channel());
}
private String getCurrentFormatTime() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public void channelRead0(ChannelHandlerContext ctx, TextMessage msg) throws Exception {
MESSAGE_GROUP.forEach((k,v) ->{
try {
if(k.equals(ctx.channel().id())){
v.writeAndFlush(new TextMessage(String.format("%s: 你说: %s",getCurrentFormatTime(),msg.text())));
}else{
v.writeAndFlush(new TextMessage(String.format("%s: 客户端[%s]说: %s",getCurrentFormatTime(),ctx.channel().id(),msg.text())));
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
@Override
public void exceptionCatch(ChannelHandlerContext ctx, Throwable throwable) throws Exception {
MESSAGE_GROUP.forEach((k,v) ->{
try {
if(!k.equals(ctx.channel().id())){
v.writeAndFlush(new TextMessage(String.format("%s: 客户端[%s]离开了消息组",getCurrentFormatTime(),ctx.channel().id())));
}
} catch (Exception e) {
e.printStackTrace();
}
});
MESSAGE_GROUP.remove(ctx.channel().id());
}
}
编写客户端代码
public static void csMessageClient() throws Exception {
BootStrap bootStrap = new BootStrap();
Channel channel = bootStrap
.group(new DefaultEventLoopGroup())
.handler(new ChannelInitializeHandler() {
@Override
public void initializer(Channel ch) throws Exception {
ch.pipeline()
.addLast(new CsMessageCodec())
.addLast(new CsClientMessageIdleStateHandler())
.addLast(new SimpleChannelAdapterHandler<TextMessage>() {
@Override
public void channelRead0(ChannelHandlerContext ctx, TextMessage msg) throws Exception {
System.out.println(new String(msg.getBody()));
}
});
}
}).connect(new InetSocketAddress("localhost", 8080));
Scanner scanner = new Scanner(System.in);
System.out.println("请发送消息");
while(scanner.hasNext()){
String msg = scanner.nextLine();
channel.writeAndFlush(new TextMessage(msg,true));
}
}
测试一个客户端连接到服务端的效果
可以看到,客户端接收到了服务端响应的消息
这个时候我们加一个客户端,测试两个客户端的效果
客户端1收到了客户端2的上线消息,同时客户端2也收到了服务端响应的消息
客户端1
客户端2
测试客户端1和2分别发送消息
客户端1和2分别收到了消息
关闭客户端2
客户端1也收到了客户端2的离线通知
由此,我们实现了一个简单的群聊功能,同时附带了心跳检测功能