Netty快速入门(五)

## Netty实现一个群聊系统
要求:

1.使用netty实现一个群聊系统,实现服务器端与客户端的非阻塞通信
2.实现多人群聊
3.可以检测用户上线离线并实现消息转发

服务器端代码

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;
/**
 * 群聊系统
 * */
public class ChatServer {
    private int port;
    public ChatServer(int port){
        this.port=port;
    }
    public void run(){
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup work =  new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss,work)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //解码器
                            pipeline.addLast("decode",new StringDecoder());
                            pipeline.addLast("encode",new StringEncoder());
                            //pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
                            pipeline.addLast(new ServerHandler());
                        }
                    });
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("服务器已就绪");
            future.channel().closeFuture().sync();
            System.out.println("服务器已关闭");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }
    public static void main(String[] args) {
        new ChatServer(8963).run();
    }
}
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.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;

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

public class ServerHandler extends SimpleChannelInboundHandler<String> {
    //管理channel的组
    private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * 客户端连接上来后输出登录服务器
     * */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        String time = simpleDateFormat.format(new Date());
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+"["+time+"]"+"已登录服务器");
    }
    /**
     * 新的客户端连接调用此方法通知所有在线用户
     * */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        String time = simpleDateFormat.format(new Date());
        Channel channel = ctx.channel();
        channels.writeAndFlush("[系统]"+"["+time+"]"+channel.remoteAddress()+"已上线");
        channels.add(channel);
    }
    /**
     * 客户端离开群聊后此方法通知所有在线用户
     * */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String time = simpleDateFormat.format(new Date());
        Channel channel = ctx.channel();
        channels.writeAndFlush("[系统]"+time+channel.remoteAddress()+"已下线");
    }
    /**
     * 客户端关闭连接调用此方法
     * */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String time = simpleDateFormat.format(new Date());
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+time+"已断开服务器");
    }
    /**
     * 读取用户发送的消息操作
     * */
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        String time = simpleDateFormat.format(new Date());
        Channel channel = channelHandlerContext.channel();
        channels.forEach(c->{
            if(c!=channel){
                c.writeAndFlush("[来自"+channel.remoteAddress()+"消息]"+":   "+s);
            }
        });
    }
}

## ```客户端

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


public class ChatClient {
    private String addr;
    private int port;
    public ChatClient(String addr,int port){
        this.addr = addr;
        this.port = port;
    }
    public void run(){
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("decode",new StringDecoder());
                            pipeline.addLast("encode",new StringEncoder());
                            pipeline.addLast(new ClientHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect(addr, port).sync();
            Channel channel = future.channel();
            Scanner scanner = new Scanner(System.in);
            while(scanner.hasNext()){
                channel.writeAndFlush(scanner.nextLine());
            }
            future.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ChatClient("127.0.0.1",8963).run();
    }
}

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

public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s);
    }
}

测试

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

私聊的简单实现思路

群聊系统使用的是private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); ChannelGroup管理登录的用户,很容易实现消息转发。如果是私聊可以使用自定义数据结构存储用户信息,有用户唯一标识以及对应channel即可。需要私聊获取到对应channel则可以发送消息了

Netty心跳检测

Netty框架心跳检测可以直接添加到pipeiline中就可以了
在这里插入图片描述

IdleStateHandler是Netty提供的心跳检测类
readerIdleTime是多长时间没有读
writeIdleTime是多长时间没有写
allIdleTime是多长时间没有进行读或写
需要在下一个pipeiline中的userEventTriggered方法中进行重写
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值