Netty(七)之聊天室小小小案例

需求

1)上線或者下線給其它人員通知

2)A發送消息其它人員都可見

設計思路

客戶端與服務端建立連接後會觸發 serverHandler中的 channelActive  方法,把channel保存到ChannelGroup中,當客戶端給服務端發送消息時,把channelGroup中的每一個channel都把消息發送一遍,就實現羣發功能

 

代碼實現(親測可用)

pom

<dependencies>
        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>

    </dependencies>

MyChatServerHandler

package mychat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * @author CBeann
 * @create 2019-10-16 15:55
 */
public class MyChatServerHandler extends SimpleChannelInboundHandler<String> {


    //用一個ChannelGroup保存所有連接到服務器的客戶端通道
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {

        Channel channel = channelHandlerContext.channel();

        //服務器收到消息
        // "[服務端]   " + channel.remoteAddress() + "通道關閉";
        String body = s;

        //羣發
        channelGroup.forEach((x) -> {
            if (x != channel) {
                x.writeAndFlush(channel.remoteAddress() + "說===>" + s);
            } else {
                x.writeAndFlush("自己說===>" + s);
            }
        });


    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        String notice = "[服務端]   " + channel.remoteAddress() + "通道激活";
        System.out.println(notice);
        channelGroup.writeAndFlush(notice);
        //添加建立連接的channel
        channelGroup.add(channel);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //刪除失效的channel
        channelGroup.remove(channel);
        String notice = "[服務端]   " + channel.remoteAddress() + "通道關閉";
        channelGroup.writeAndFlush(notice);
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("[服務端]   " + channel.remoteAddress() + "出現異常");
        ctx.close();
    }
}

MyChatServer

package mychat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * @author CBeann
 * @create 2019-10-16 15:51
 */
public class MyChatServer {

    public static void main(String[] args) {

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //TimeClientHandler是自己定義的方法
                            socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                            socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                            socketChannel.pipeline().addLast(new MyChatServerHandler());
                        }
                    });
            //綁定端口
            ChannelFuture f = b.bind(8888).sync();
            //等待服務端監聽端口關閉
            f.channel().closeFuture().sync();


        } catch (
                Exception e) {

        } finally {
            //優雅關閉,釋放線程池資源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }


    }
}

MyChatClientHandler

package mychat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @author CBeann
 * @create 2019-10-16 21:23
 */
public class MyChatClientHandler extends SimpleChannelInboundHandler<String> {


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {


        //收到服務端發送的消息
        String body = s;
        System.out.println(body);


    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("[客戶端出現異常");
        ctx.close();
    }
}

MyChatClient

package mychat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;

/**
 * @author CBeann
 * @create 2019-10-16 21:23
 */
public class MyChatClient {


    public static void main(String[] args) throws Exception {
        int port = 8888;
        String host = "127.0.0.1";
        //配置客戶端NIO線程組
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {

                            socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                            socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                            //TimeClientHandler是自己定義的方法
                            socketChannel.pipeline().addLast(new MyChatClientHandler());
                        }
                    });
            //發起異步連接操作
            ChannelFuture f = b.connect(host, port).sync();


//            //發送數據
            Scanner reader = new Scanner(System.in);
            String body = reader.nextLine();
            while (!"exit".equals(body)) {
                f.channel().writeAndFlush(body);
                body = reader.nextLine();
            }


            //等待客戶端鏈路關閉
            f.channel().closeFuture().sync();

        } catch (Exception e) {

        } finally {
            //優雅關閉
            group.shutdownGracefully();
        }
    }
}

 

常見問題

1)IDEA怎麼把一個啓動類同時運行多次

先運行一下程序,在按照下面的操作進行

https://blog.csdn.net/qq_39387856/article/details/87170301

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章