网络编程利器之Netty专题(四)

简单的聊天

服务端

ChatServer

public class ChatServer {

    private int port;

    public ChatServer(int port) {
        this.port = port;
    }

    public void run() {
        //‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,
        // 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //服务端的启动时利用ServerBootstrap,而客户端的启动时利用Bootstrap
        try{
            ServerBootstrap strap = new ServerBootstrap();
            strap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChatServerInitializer())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            //到这里服务器就启动了
            System.out.println("[系统消息]: 服务器启动完毕!");

            //开始接受客户端连接
            ChannelFuture sync = strap.bind(port).sync();

            //等待服务器关闭
            sync.channel().closeFuture().sync();
        }catch (Exception e){

        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            System.out.println("[系统消息]: 服务器关闭!");
        }
    }

    public static void main(String[] args) throws Exception{
        int port = 8080;
        new ChatServer(port).run();
    }
}

ChatServerInitializer

public class ChatServerInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //获取通道,设置通道的初始化属性
        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new ChatServerHandler());

        System.out.println("SimpleChatClient:"+ socketChannel.remoteAddress() +"连接上");
    }
}

ChatServerHandler

/**
 * 服务端的事件处理器,指定泛型为String,表示处理的是String类型数据
 */
public class ChatServerHandler extends SimpleChannelInboundHandler<String>{

    //新建一个channels,用于存放连接的channel
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 每当从服务端收到新的客户端连接时,客户端的 Channel 存入ChannelGroup列表中,并通知列表中的其他客户端 Channel
     * channelGroup存放连接的channel
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel inComing = ctx.channel();
        for(Channel channel : channels){
            //不是当前channel,通知其它channel
            if(channel != inComing){
                channel.writeAndFlush("[系统消息]: " + inComing.remoteAddress() + "上线了!\n");
            }
        }
        //把当前的channel加入到channels
        channels.add(inComing);
    }

    /**
     * 当有服务端收到客户端断开连接时,客户端的 Channel 存入ChannelGroup列表中,并通知列表中的其他客户端 Channel
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel inComing = ctx.channel();
        for(Channel channel : channels){
            //不是当前channel,通知其它channel
            if(channel != inComing){
                channel.writeAndFlush("[系统消息]: " + inComing.remoteAddress() + "下线了!\n");
            }
        }
        //把当前的channel加入到channels
        channels.remove(inComing);
    }

    /**
     * 每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            if (channel != incoming){
                channel.writeAndFlush("[用户 " + incoming.remoteAddress() + "]: " + msg + "\n");
            } else {
                channel.writeAndFlush("[我]: " + msg + "\n");
            }
        }
    }

    /**
     * 当服务端监听到客户端活动时
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel inComing = ctx.channel();
        System.out.println("[" +inComing.remoteAddress()+ "]: "+"在线中");
    }

    /**
     * 当服务端监听到客户端不活动时
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel inComing = ctx.channel();
        System.out.println("[" +inComing.remoteAddress()+ "]: "+"离线中");
    }

    /**
     * 当发生io异常时
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
        // 当出现异常就关闭连接
        System.out.println("出现异常");
        ctx.close();
    }
}

客户端

ChatClient

public class ChatClient {

    private String host;
    private int port;

    public ChatClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void run(){
        //客户端用Bootstrap来启动
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChatClientInitializer());

            //连接后返回一个当前客户端的channel
            Channel channel = bootstrap.connect(host, port).sync().channel();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            //一直处于输入状态
            while (true) {
                channel.writeAndFlush(reader.readLine() + "\n");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args){
        String host = "localhost";
        int port = 8080;
        new ChatClient(host, port).run();
    }
}

ChatClientInitializer

public class ChatClientInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //获取通道,设置通道的初始化属性
        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new ChatClientHandler());
    }
}

ChatClientHandler

public class ChatClientHandler extends SimpleChannelInboundHandler<String>{

    /**
     * 客户端接受到消息,打印到自己客户端这里
     * @param channelHandlerContext
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值