需求
需求:私聊系统设计思路
服务端:
无需群聊系统中的channelGroup 当前代码含有 懒得剔除 (主要实现为私聊系统channelMap)
channelMap:key:当前登录用户的唯一id :port value:Channel (id为客户端端口)注意敲定唯一
客户端加入服务器时调用的方法-即加入
handlerAdded->channelMap.put(socketAddress.getPort(), channel); 存入对应channel
发送消息时:去选择
channelRead0->channelMap.get(Integer.valueOf(selectUserId));获取对应channel
客户端:
发送过来的消息模型为:
例如:61311&你好瑶瑶 (61311为对方的私聊端口)
InetSocketAddress socketAddress = (InetSocketAddress)channel.localAddress();重要
也可以自己给自己聊
服务端
/**
* 功能描述: 服务器
*
* @author Songxianyang
* @date 2023-06-04 17:38
*/
public class ChartNettyServer {
private Integer port;
public ChartNettyServer(Integer port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossEvent = new NioEventLoopGroup();
EventLoopGroup workerEvent = new NioEventLoopGroup();
try {
// 服务器端 配置参数
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossEvent,workerEvent) // 设置两个线程组
.channel(NioServerSocketChannel.class) // nio实现类通道
.option(ChannelOption.SO_BACKLOG,128) // 设置线程队列
.childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象)
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("解码器", new StringDecoder());
pipeline.addLast("编码器", new StringEncoder());
pipeline.addLast(new ChartNettyServerHandler());// 向管道追加一个处理器
}
});// 给workerGroup的EventLoop对应的管道设置处理器
System.out.println("服务器端启动完成!");
ChannelFuture cf = serverBootstrap.bind(port).sync(); // 绑定端口,并且同步,生成了一个ChannelFuture对象
// 注册监听器 到 ChannelFuture。从而获取 异步回调
cf.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
System.out.println("监听成功:::》》》》port:"+port);
}else {
System.out.println("监听失败");
}
});
cf.channel().closeFuture().sync(); // 对关闭通道进行见监听
} finally {
bossEvent.shutdownGracefully(); // 关闭
workerEvent.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
ChartNettyServer chartNettyServer = new ChartNettyServer(7000);
chartNettyServer.run();
}
}
服务端处理器
/**
*
* @author Songxianyang
* @date 2023-06-04 17:48
* 自定义一个处理器,需要继承netty规定好的某个HandlerAdapter
*/
@Slf4j
public class ChartNettyServerHandler extends SimpleChannelInboundHandler<String> {
/**
* key:当前登录用户的唯一id :port value:Channel
*/
private static Map<Integer, Channel> channelMap = new HashMap<>();
//定义一个channle 组,管理所有的channel
//GlobalEventExecutor.INSTANCE) 是全局的事件执行器,是一个单例
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
// 当前 客户端 channel
Channel channel1 = channelHandlerContext.channel();
System.out.println("获取当前客户端消息为:"+s);
// 选择的用户 进行私聊开始
String[] split = s.split("&");
String selectUserId = split[0];
String msg = split[1];
Channel channel = channelMap.get(Integer.valueOf(selectUserId));
System.out.println("消息已推送给私聊用户为:"+">>>>>>"+channel.remoteAddress());
System.out.println("对方用户id为:"+">>>>>>"+channel.hashCode());
channel.writeAndFlush(">>>>>"+channel1.remoteAddress()+"发送过来的消息为::::"+msg);
}
//表示channel 处于活动状态, 提示 上线
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("---------------"+ctx.channel().remoteAddress()+"--------------------"+"上线!开始聊天吧!");
}
//表示channel 处于不活动状态, 提示 xx离线了
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("---------------"+ctx.channel().remoteAddress()+"--------------------"+"离线!");
}
/**
* C、S端一单建立Channel连接,就会触发该方法
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// 给其他客户端发送某某加入群聊
Channel channel = ctx.channel();
InetSocketAddress socketAddress = (InetSocketAddress)channel.remoteAddress();
// 获取客户端发channel。hashcode
System.out.println("客户端channel.id=="+socketAddress.getPort());
channelGroup.writeAndFlush("客户端:》》》》" + socketAddress + "已加入群聊系统,注意隐士安全!可以发起私聊欧!");
channelGroup.add(channel);
// 放入私聊channel
log.info("channel.remoteAddress()----》{}",channel.remoteAddress());
log.info("handlerAdded: 获取唯一标识绑定channel----》{}",socketAddress.getPort());
channelMap.put(socketAddress.getPort(), channel);
}
/**
* Channel 下线,通知其他 客户端
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("客户端:》》》》" + channel.remoteAddress() + "退出群聊系统");
System.out.println("在线用户为="+channelGroup.size());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
客户端
/**
* 功能描述: 客户端
*
* @author Songxianyang
* @date 2023-06-04 18:00
*/
public class ChartNettyClient {
private Integer port;
private String ip;
public ChartNettyClient(Integer port, String ip) {
this.port = port;
this.ip = ip;
}
private void run() throws InterruptedException {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("解码器", new StringDecoder());
pipeline.addLast("编码器", new StringEncoder());
pipeline.addLast(new ChartNettyClientHandler());
}
});
System.out.println("客户端 is ok...");
ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
Channel channel = channelFuture.channel();
InetSocketAddress socketAddress = (InetSocketAddress)channel.localAddress();
System.out.println("获取客户端端口-----"+socketAddress.getPort()+"-------port--------");
System.out.println("客户端地址------"+channel.remoteAddress()+"-----");
System.out.println("用户id为:------"+socketAddress.getPort()+"-----");
//客户端需要创建消息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String msg = scanner.nextLine();
//通过channel发送到服务器端
channel.writeAndFlush(msg + "\n");
}
channel.closeFuture().sync();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new ChartNettyClient(7000, "127.0.0.1").run();
}
}
客户端处理器
/**
* 功能描述:
*
* @author Songxianyang
* @date 2023-06-04 18:11
*/
public class ChartNettyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println("------>"+s.trim()+"<--------");
}
}
测试截图:
S
C