实现场景: 聊天

服务端,客户端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 上线

Netty 多客户端连接与通信

 

2、再启动一个客户端

可以发现60635 上线,

Netty 多客户端连接与通信

 

并且第一个客户端提示 60635 加入

Netty 多客户端连接与通信

 

3、再启动一个客户端

提示60966上线

Netty 多客户端连接与通信

 

第一个客户端 提示60966 加入

Netty 多客户端连接与通信

第二个客户端 提示60966 加入

Netty 多客户端连接与通信

 

四、测试2

1、客户端A发送消息: 大家好,我是客户端A

Netty 多客户端连接与通信

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

 

客户端B接收到信息

Netty 多客户端连接与通信

 

客户端C接收到的信息

Netty 多客户端连接与通信

 

自此,实现了通过多个Socket实现的通信的过程

相关文章:

  • 2022-12-23
  • 2021-09-24
  • 2021-06-06
  • 2022-12-23
  • 2021-06-13
  • 2021-05-30
  • 2021-10-22
  • 2022-12-23
猜你喜欢
  • 2021-12-06
  • 2021-08-11
  • 2021-10-11
  • 2022-12-23
  • 2021-05-02
  • 2022-01-01
  • 2022-12-23
相关资源
相似解决方案