Netty 多客戶端連接與通信


實現場景: 聊天

服務端,客戶端A,客戶端B,客戶端C。當客戶端發送消息給服務端后,服務端在將這條消息廣播個所有客戶端戶端A,客戶端B,客戶端C。

需求1: 客戶端上線后,會通知所有客戶端上線。

如客戶端A先建立連接,不需要通知。

當客戶端B與服務端建立連接,服務端告訴A,客戶端B上線。

A和B建立連接后,客戶端C和服務端建立連接。服務端廣播一條信息給A和B。

需求2: A、B、C都已經建立連接,當A發送一條給服務端,服務端廣播這條消息給所有客戶端,客戶端A會提示這條消息是自己發送的。

 

一、服務端程序的編寫

1、MyChartServer 類

public class MyChartServer {
    public static void main(String[] args) throws  Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new MyChatServerInitializer());

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

  

  

2、MyChatServerInitializer 類

public class MyChatServerInitializer extends ChannelInitializer<SocketChannel>{

    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyChartServerHandle());
    }
}

  

 

3、MyChartServerHandle 類

public class MyChartServerHandle extends SimpleChannelInboundHandler<String>{


    //用於保存所有Channel對象
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //channel為當前客戶端發給服務端的channel
        Channel channel = ctx.channel();
        channelGroup.forEach(ch -> {

            if(channel != ch){
                ch.writeAndFlush(channel.remoteAddress() + " 發送的消息:" + msg + "\n");
            } else{
                ch.writeAndFlush("[自己]" + msg + " \n");
            }
        });

    }

    //表示連接建立
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
       //chanel可以理解成Connection
        Channel channel = ctx.channel();
        //廣播消息給所有的客戶端
        channelGroup.writeAndFlush("[服務器] - " + channel.remoteAddress() + " 加入\n");
        channelGroup.add(channel);
    }

    //表示連接斷掉了
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //廣播消息給所有的客戶端
        channelGroup.writeAndFlush("[服務器] - " + channel.remoteAddress() + " 離開\n");
        //下面這行代碼Netty會自動調用
        //channelGroup.remove(channel);
    }

    //表示連接時活動狀態
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //廣播消息給所有的客戶端
        CommonUtil.println( channel.remoteAddress() + " 上線 \n");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //廣播消息給所有的客戶端
        CommonUtil.println( channel.remoteAddress() + " 下線 \n");
    }

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

  

  二、客戶端程序編寫

1、MyChatClient類

public class MyChatClient {
    public static void main(String[] args) throws  Exception{
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new MyChatClientInitializer());

            //channel表示與服務端的Connection
            Channel channel = bootstrap.connect("localhost",8899).sync().channel();
            //不斷讀取客戶端輸入的內容
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

            for(;;){
                channel.writeAndFlush(br.readLine() + "\r\n");
            }

        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}

  

  2、MyChatClientInitializer  類

public class MyChatClientInitializer  extends ChannelInitializer<SocketChannel> {



    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyChartClientHandle());
    }
}

  

3、MyChartClientHandle 類

public class MyChartClientHandle extends SimpleChannelInboundHandler<String> {

    // 對於客戶端來說,輸入來自控制台
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //僅僅打印來自服務端的信息
        CommonUtil.println(msg);

    }


}

  

三、測試

1、啟動MyChartServer,然后啟動MyChatClient

MyChartServer打印 59786 上線

 

2、再啟動一個客戶端

可以發現60635 上線,

 

並且第一個客戶端提示 60635 加入

 

3、再啟動一個客戶端

提示60966上線

 

第一個客戶端 提示60966 加入

第二個客戶端 提示60966 加入

 

四、測試2

1、客戶端A發送消息: 大家好,我是客戶端A

客戶端A接收到寫信息:  [自己]大家好,我是客戶端A 

 

客戶端B接收到信息

 

客戶端C接收到的信息

 

自此,實現了通過多個Socket實現的通信的過程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM