netty学习(三) -基于socket实现消息通讯(聊天室,广播)

4 篇文章 0 订阅

目标

  1. 每一个客户端连接上服务器的时候需要给其它客户端发送消息。
  2. 当前客户端可以发送消息给所有连接上了的客户端。

服务端

public class WeChatServer {

    private static final Logger logger = LoggerFactory.getLogger(WeChatServer.class);

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boosGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    logger.info("有客户端连接到服务器:{}", ch.remoteAddress());
                    ChannelPipeline pipeline = ch.pipeline();
                    // 这个解码器的作用可以按照指定的分隔符进行分包
                    // Delimiters.lineDelimiter() 换行分隔符 \r\n
                    pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter()));
                    pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
                    pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
                    // 注入自定义的处理器
                    pipeline.addLast(new WeChatServerHandler());

                }
            });
            ChannelFuture channelFuture = bootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

public class WeChatServerHandler extends SimpleChannelInboundHandler<String> {

	// 需要注意的是这个对象要设置成为static 否则只能收到自己的消息,不能收到别人的消息
    private static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel channel = ctx.channel();
        // 需要注意的是转发消息的时候要在后面加一个\n,因为我们在这个处理器的前面加了一个分隔符处理器DelimiterBasedFrameDecoder
        channelGroup.forEach(ch -> {
            if (ch == channel) {
                ch.writeAndFlush("[自己] " + msg + "\n");
            } else {
                ch.writeAndFlush("[" + channel.remoteAddress() + "]" + msg + "\n");
            }
        });
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush("[服务器]- " + ctx.channel().remoteAddress() + " 加入\n");
        channelGroup.add(channel);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        channelGroup.writeAndFlush("[服务器]- " + ctx.channel().remoteAddress() + " 离开\n");
		// 这里不需要手动从channelGroup 中移除当前通道
		// 因为channelGroup 在初始化的时候使用的GlobalEventExecutor.INSTANCE会帮我们去干这个事情
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + " 上线!");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + " 下线!");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

客户端

public class WeChatClient {

    private static final Logger logger = LoggerFactory.getLogger(WeChatClient.class);

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter()));
                    pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
                    pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
                    pipeline.addLast(new WeChatClientHandler());
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            Scanner scanner = new Scanner(System.in);
            while(scanner.hasNext()){
                String next = scanner.next();
                channelFuture.channel().writeAndFlush(next+"\r\n");
            }
            channelFuture.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}
public class WeChatClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg);
    }
}

实现效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

channelGroup的作用

  1. channelGroup 是netty为我们提供的持有channel的容器,通过这个对象 可以实现给一批客户端推送消息。
  2. 它实现了Set接口,所以我们可以像处理集合一样丝滑的处理它,比如 forEach、filter等等。

DelimiterBasedFrameDecoder的作用

它可以按照我们指定的规则,对数据包进行切割,保证每次接收到的报文都是完整的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值