1.服务端实现
我们通过前期知识了解到,netty主要保证请求的数据和发送方想要发送的数据是一样的,对数据要进行业务处理,主要靠不同的处理器。
这里实现群聊功能主要靠两个处理器,创建群聊处理器和发送群聊消息处理器。下面将介绍每个处理器处理的业务。
1.创建群聊处理器
这里创建群聊处理器主要就是收到某个用户发送的创建群聊请求后,处理请求的主要步骤为:
1.拿到群聊名称
2.拿到群聊初始化成员
3.创建群聊
4.给创建群聊的用户发送创建成功的消息
5.给群聊里面的用户发送,你已被拉入群聊的消息。
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import message.GroupCreateRequestMessage;
import message.GroupCreateResponseMessage;
import server.session.Group;
import server.session.GroupSession;
import server.session.GroupSessionFactory;
import java.util.List;
import java.util.Set;
/**
* 组创建请求消息处理程序
*
* @date 2023/12/04--08:49:25
*/
@ChannelHandler.Sharable
public class GroupCreateRequestMessageHandler extends SimpleChannelInboundHandler<GroupCreateRequestMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, GroupCreateRequestMessage msg) throws Exception {
//获得群的名字
String groupName = msg.getGroupName();
//得到群初始化的一些人员
Set<String> members = msg.getMembers();
//创建群聊
GroupSession groupSession = GroupSessionFactory.getGroupSession();
Group group = groupSession.createGroup(groupName, members);
//创建成功,就像创建群的人发送消息创建成功,还要给这个群里面其他的成员发送消息
if (group==null){
ctx.writeAndFlush(new GroupCreateResponseMessage(true,"创建成功"));
List<Channel> membersChannel = groupSession.getMembersChannel(groupName);
membersChannel.forEach(channel -> {
channel.writeAndFlush(new GroupCreateResponseMessage(true,"你已被拉入"+groupName+"群"));
});
}else {
ctx.writeAndFlush(new GroupCreateResponseMessage(false,"创建失败:"+groupName+"已经存在"));
}
}
}
2.群聊天消息处理器
发送群聊消息这里的实质就是给在这个群里面的每个人都发送了一个消息(排除自己,但是这里没有实现,这个比较简单,可以自行实现),下面是主要实现的步骤:
1.拿到发送消息的用户名
2.拿到群聊的名称
3.根据群聊的名称,通过一个群聊会话管理器,拿到这个群里面的所有人的在线连接
4.给这个群里面的每个人发送消息(发送者+消息)
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import message.GroupChatRequestMessage;
import message.GroupChatResponseMessage;
import server.session.GroupSessionFactory;
import java.util.List;
@ChannelHandler.Sharable
public class GroupChatRequestMessageHandler extends SimpleChannelInboundHandler<GroupChatRequestMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, GroupChatRequestMessage msg) throws Exception {
List<Channel> channels = GroupSessionFactory.getGroupSession()
.getMembersChannel(msg.getGroupName());
for (Channel channel : channels) {
channel.writeAndFlush(new GroupChatResponseMessage(msg.getFrom(), msg.getContent()));
}
}
}
2.客户端实现
客户端的代码基本没有改变
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import message.*;
import protocol.MessageCodecSharable;
import protocol.ProcotolFrameDecoder;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class ChatClient {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup();
LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();
CountDownLatch LOGIN_WAIT=new CountDownLatch(1);
try {
final boolean[] flag = {false};
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(group);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProcotolFrameDecoder());
ch.pipeline().addLast(LOGGING_HANDLER);
ch.pipeline().addLast(MESSAGE_CODEC);
ch.pipeline().addLast("clientHandler",new ChannelInboundHandlerAdapter(){
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
executorService.execute(()->{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名");
String username = scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();
LoginRequestMessage message = new LoginRequestMessage(username, password);
ctx.writeAndFlush(message);
System.out.println("等待");
try {
LOGIN_WAIT.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//登录失败
if (!flag[0]){
System.out.println("登录失败");
ctx.channel().close();
return;
}
//登录成功
System.out.println("登录成功");
while (true){
System.out.println("========");
System.out.println("send [to] [content]");
String s = scanner.nextLine();
String[] s1 = s.split(" ");
switch (s1[0]){
case "send":
ctx.writeAndFlush(new ChatRequestMessage(username, s1[1], s1[2]));
break;
case "gsend":
ctx.writeAndFlush(new GroupChatRequestMessage(username, s1[1], s1[2]));
break;
case "gcreate":
Set<String> set = new HashSet<>(Arrays.asList(s1[2].split(",")));
set.add(username); // 加入自己
ctx.writeAndFlush(new GroupCreateRequestMessage(s1[1], set));
break;
case "gmembers":
ctx.writeAndFlush(new GroupMembersRequestMessage(s1[1]));
break;
case "gjoin":
ctx.writeAndFlush(new GroupJoinRequestMessage(username, s1[1]));
break;
case "gquit":
ctx.writeAndFlush(new GroupQuitRequestMessage(username, s1[1]));
break;
case "quit":
ctx.channel().close();
return;
default:
System.out.println("我还没有这个功能");
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof LoginResponseMessage){
LoginResponseMessage message= (LoginResponseMessage) msg;
if (message.isSuccess()){
flag[0] =true;
}
LOGIN_WAIT.countDown();
}
log.debug("{}",msg);
}
});
}
});
Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
channel.closeFuture().sync();
} catch (Exception e) {
log.error("client error", e);
} finally {
group.shutdownGracefully();
}
}
}
这是这个项目的gitee地址:https://gitee.com/CGM666/netty-demo.git
这个项目还在不断更新中。