Netty-6 实现群聊功能

  1. GroupChatServer
  2. GroupChatServerHandler
  3. GroupChatClient
  4. GroupChatClientHandler

GroupChatServer

package netyy.simple.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

/**
 * @Description TODO
 * @Author hxy
 * @Date 2021/3/17 13:43
 * @Version 1.0
 */
public class GroupChatServer {

    //编写服务端

    //配置端口号
    private int PROT;//端口号


    public GroupChatServer(int PROT) {
        this.PROT = PROT;
    }

    //编写run方法处理客户端请求
    public void run() throws InterruptedException {
        //1.new 两个线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try{
            //2.创建ServerBootstrap对象 也就是服务器启动的引导类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
                    //2.1group 用来设置两个线程组
            serverBootstrap.group(bossGroup,workGroup)
                    // 2.2用来设置一个服务器端的通道实现
                    .channel(NioServerSocketChannel.class)
                    //2.3 用来给ServerChannel添加配置 设置阻塞线程的队列大小为128个
                    .option(ChannelOption.SO_BACKLOG,128)
                    //2.4 用来给接收到的通道添加配置 设置通道为一直活跃
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    //2.5 用来设置业务处理类(自定义的Handler)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //获取到pipeline
                            ChannelPipeline pipeline = ch.pipeline();
                            //向pipeline加入解码器
                            pipeline.addLast("decoder",new StringDecoder());
                            //向pipeline加入编码器
                            pipeline.addLast("encoder",new StringEncoder());
                            //加入自己的业务处理hanlder
                            pipeline.addLast(new GroupChatServerHandler());
                        }
                    });
            System.out.println("Netty 服务器启动");
            //3. 设置端口号 并且设置为同步
            ChannelFuture cf = serverBootstrap.bind(PROT).sync();//设置为同步
            System.out.println("Netty 服务器启动成功,端口:"+PROT);
            //监听关闭
            cf.channel().closeFuture().sync();
        }finally {
            //4.优雅的关闭线程
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        //启动项目
        new GroupChatServer(7000).run();
    }
}

GroupChatServerHandler

package netyy.simple.groupchat;

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;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Description TODO
 * @Author hxy
 * @Date 2021/3/17 14:00
 * @Version 1.0
 */
//创建业务处理Handler
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {

    //定义一个ChannelGroup,管理所有的Channel
    // GlobalEventExecutor.INSTANCE是全局的事件执行器,是一个单例  由于每一个客户端都会有一个Handler 所以这里为静态修饰
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    //时间格式
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    //handlerAdded 表示连接建立,一但连接,第一个被执行的方法
    //将当前Channel 加入到channelGroup中
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        //每一个连接进来的用户 都会触发该方法 所以可以将它的通道加入到channelGroup通道组中
        //也就意味着是当前连接的所有用户
        Channel channel = ctx.channel();
        //将用户上线的消息推送给其他在线的客户端
        //该方法会将channelGroup 中所有的channel 遍历,并发送我们指定的消息
        //不需要我们自己遍历
        channelGroup.writeAndFlush(sdf.format(new Date()) + "[用户" + channel.remoteAddress() + "]" + ":" + " 加入聊天\n");
        channelGroup.add(channel);
    }

    //表示channel 处于活动状态,提示**上线
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(sdf.format(new Date()) + "-" + ctx.channel().remoteAddress() + "上线\n");
    }

    //表示channel 处于不活动状态,会触发该方法
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(sdf.format(new Date()) + "-" + ctx.channel().remoteAddress() + "离线\n");
    }

    //断开连接时触发 ,将**用户离线的信息发送给当前客户
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush(sdf.format(new Date()) + "[用户" + channel.remoteAddress() + "]" + ":" + " 离开\n");
        //当触发该方法的时候 会自动将该handler剔除channelGroup
        //所以不需要手动remove
        System.out.println("当前在线人数:" + channelGroup.size());
    }

    //通道有数据时触发
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //获取到当前channel
        Channel channel = ctx.channel();
        //这时我们便利ChannelGroup,根据不同的情况,回送不同的数据
        channelGroup.forEach(ch -> {
            if (channel != ch) {//不是当前的channel,转发消息
                ch.writeAndFlush(sdf.format(new Date()) + "[用户" + channel.remoteAddress() + "]" + ":" + msg + "\n");
            } else {//回显
                ch.writeAndFlush(sdf.format(new Date()) + "[我]:" + msg + "\n");
            }

        });
    }

    //发生异常时触发
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //关闭通道
        ctx.close();
    }
}

GroupChatClient

package netyy.simple.groupchat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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 java.util.Scanner;

/**
 * @Description TODO
 * @Author hxy
 * @Date 2021/3/17 14:43
 * @Version 1.0
 */
public class GroupChatClient {

    //属性
    private final String HOST;
    private final int PORT;

    public GroupChatClient(String HOST, int PORT) {
        this.HOST = HOST;
        this.PORT = PORT;
    }

    public void run() throws InterruptedException {

        //客户端只需要一个事件循环组即可

        EventLoopGroup group = new NioEventLoopGroup();

        try {
            //创建一个客户端启动引导类
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(group)//设置线程组
                    .channel(NioSocketChannel.class)//设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //向pipeline加入解码器
                            ch.pipeline().addLast("decoder", new StringDecoder());
                            //向pipeline加入编码器
                            ch.pipeline().addLast("encoder", new StringEncoder());

                            ch.pipeline().addLast(new GroupChatClientHandler());//加入自己的处理器
                        }
                    });


            //启动客户端去连接服务器端
            //关于ChannelFutuer 分析,涉及到netty的异步模型
            ChannelFuture cf = bootstrap.connect(HOST, PORT).sync();
            Channel channel = cf.channel();

            System.out.println("------------" + channel.localAddress() + "----------");
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                channel.writeAndFlush(scanner.nextLine() + "\r\n");
            }

        } finally {
            group.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        new GroupChatClient("127.0.0.1", 7000).run();
    }
}

GroupChatClientHandler

package netyy.simple.groupchat;

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

/**
 * @Description TODO
 * @Author hxy
 * @Date 2021/3/17 14:43
 * @Version 1.0
 */
public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> {


    //通道有读取数据时触发
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg.trim());

    }
}

依次启动服务器和客户端,在客户端控制台发送消息即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值