《Netty学习打卡--从小白到放弃》----- 05 - netty实现简单的聊天功能

本文介绍使用Netty框架实现简单聊天室的过程,包括服务器端与客户端的代码解析,重点讲解了ChannelGroup的运用,实现消息广播及用户上下线通知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

打卡日期(2019-07-05)

netty实现简单的聊天程序

    在编写程序前需要了解netty 的channel执行流程,以及ChannelGroup的概念

channel 执行流程
-   1.handlerAdded 一个新的处理器上添加通道
-   2.channelRegistered 将通道注册到某个对象上
-   3.channelActive 通道处于活跃状态
-   4.channelRead0 通过通道开始处理IO请求
-   5.channelReadComplete 通道读取请求内容完毕并且返回信息
-   6.channelInactive 通道处于不活跃状态
-   7.channelUnregistered 通道取消注册
-   8.handlerRemoved 处理器将通道移除
-   1.ChannelGroup 消息广播,是Channel通道的一个Set集合,负责处理各个Channel的通讯,监听各个Channel的各种状态
需求
-   1.服务器端监听每个连接的加入
-   2.当有新的连接加入的时候,通知其他channel
-   3.当某个channel下线的时候,通知其他channel
-   4.当前channel发送消息的时候,显示"自己 + 消息内容",

服务端

package com.dragon.chart.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @Description: 跟 03章,04章 的一样,并没有什么区别
 */
public class MyChartServer {

    public static void main(String[] args) throws InterruptedException {
        //事件循环组 acceptorGroup用于接收请求
        EventLoopGroup acceptorGroup = new NioEventLoopGroup();

        //事件循环组 handlerGroup用于处理请求
        EventLoopGroup handlerGroup = new NioEventLoopGroup();

        // sub-class which allows easy bootstrap of  用于轻松的启动bootstrap
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try{
            // group (acceptorGroup,handlerGroup)
            // acceptorGroup which is used for the parent (acceptor) 用于接收所有的请求
            // handlerGroup are used to handle all the events and IO 处理所有的事件和IO操作
            serverBootstrap.group(acceptorGroup,handlerGroup)
                    // NioSctpServerChannel 利用nio模式接收一个新的连接创建NioSctpChannel
                    .channel(NioServerSocketChannel.class)
                    // 自定义处理器
                    .childHandler(new MyChartServerInit());
            //绑定80端口,端口号可以自定义
            ChannelFuture future = serverBootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        }finally {
            acceptorGroup.shutdownGracefully();
            handlerGroup.shutdownGracefully();
        }

    }
}
package com.dragon.chart.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;


/**
 * @Description:
 */
public class MyChartServerInit extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /***
         * @Param maxFrameLength 解帧码的最大长度
         * @Param delimiters   分隔符
         */
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));

        /***
         * 解码器处理器
         */
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));

        /***
         * 编码器处理器
         */
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

        /***
         * 自定义处理器
         */
        pipeline.addLast(new MyChartHandler());
    }
}
package com.dragon.chart.server;

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;

/**
 * @Description:
 */
public class MyChartHandler extends SimpleChannelInboundHandler<String> {

    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private static String server = "[服务器]:";
    private static String client = "客户";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("服务器接收到消息;【"+msg+"】");
        Channel ch = ctx.channel();
        for(Channel channel : channelGroup){
            if(ch != channel){
                channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
            }else{
                channel.writeAndFlush("[自己]:"+msg +"\n");
            }
        }
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(server + channel.remoteAddress() + client + ",加入\n" );
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(server + channel.remoteAddress() + client + ",退出\n" );
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channel.writeAndFlush(server + channel.remoteAddress() + client + ",已上线\n" );
        channelGroup.add(channel);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channel.writeAndFlush(server + channel.remoteAddress() + client + ",下线\n" );
        channelGroup.remove(channel);
    }

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

客户端

package com.dragon.chart.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @Description: 客户端
 */
public class MyChartClient {

    public static void main(String[] args) throws InterruptedException, IOException {
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try{
            bootstrap.group(clientGroup).channel(NioSocketChannel.class).handler(new MyChartClientInit());
            Channel channel = bootstrap.connect("localhost",8080).sync().channel();

            // 获取键盘输入的事件,通过通道channel将获取的内容写进通道
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            for(;;){
                channel.writeAndFlush(br.readLine() + "\r\n");
            }
        }finally {
            clientGroup.shutdownGracefully();
        }
    }
}
package com.dragon.chart.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * @Description:
 */
public class MyChartClientInit  extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /***
         * @Param maxFrameLength 解帧码的最大长度
         * @Param delimiters   分隔符
         */
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));

        /***
         * 解码器处理器
         */
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));

        /***
         * 编码器处理器
         */
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

        /***
         * 自定义处理器
         */
        pipeline.addLast(new MyChartClientHandler());
    }
}
package com.dragon.chart.client;

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

/**
 * @Description:
 */
public class MyChartClientHandler extends SimpleChannelInboundHandler<String> {


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        /***
         * 客户端不需要处理信息内容,能查看就行,所以不做任何处理,打印出来即可
         */
        System.out.println(msg);
    }
}
启动:MyChartServer
启动:MyChartClient(理解为client01)
启动:MyChartClient(理解为client02)
启动:MyChartClient(理解为client03)
MyChartServer
[服务器]:/127.0.0.1:6281客户,加入

[服务器]:/127.0.0.1:6305客户,加入

[服务器]:/127.0.0.1:6328客户,加入

MyChartClient(理解为client01)
[服务器]:/127.0.0.1:6305客户,已上线
[服务器]:/127.0.0.1:6328客户,已上线
/127.0.0.1:6328:大家好!
MyChartClient(理解为client02)
[服务器]:/127.0.0.1:6328客户,已上线
/127.0.0.1:6328:大家好!
MyChartClient(理解为client03)
输入:大家好
大家好!
[自己]:大家好!


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

此时一个简单的聊天功能实现
坑点:
1.注意服务端向客户端发送消息的时候 最后一定要加换行符号"\n",否则客户端不会显示收到的内容。
channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
channel.writeAndFlush("[自己]:"+msg +"\n");
@Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("服务器接收到消息;【"+msg+"】");
        Channel ch = ctx.channel();
        for(Channel channel : channelGroup){
            if(ch != channel){
                channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
            }else{
                channel.writeAndFlush("[自己]:"+msg +"\n");
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值