利用Netty来写一个简单的聊天室、心跳检测

很多伙伴问我是Netty怎么用,抽时间给大家写了一个demo级别的入门案例,仅供参考。

Server端代码如下:

package com.netty.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
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.concurrent.GlobalEventExecutor;

import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GroupChatServer {

    private int port;

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


    //编写一个run方法,处理客户端的请求
    public void run(){

        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        try {
            serverBootstrap.group(boosGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast("decoder", new StringDecoder());
                            socketChannel.pipeline().addLast("encoder", new StringEncoder());

                            socketChannel.pipeline().addLast(new GroupChatServerHandler());
                        }
                    });
            System.out.println("Netty 服务器启动...");

            try {
                ChannelFuture cf = serverBootstrap.bind(new InetSocketAddress(port)).sync();
                cf.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }catch (Exception e){

        }finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }



    public static void main(String[] args) {
        new GroupChatServer(6677).run();

    }
}

/**
 * 自定义消息处理
 */
class GroupChatServerHandler extends SimpleChannelInboundHandler<String>{

    //Netty提供的ChannelGroup 可以用来保存 channel
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    //保存新来的channels
    private List<Channel> channels = new ArrayList<Channel>();
    public static Map<User, Channel> channelsMap = new ConcurrentHashMap<>();

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


    /**
     * handlerAdded 表示建立链接,一旦链接,第一个被执行
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded...");
        //将该客户加入聊天的信息推送给其他在线的客户端
        channelGroup.add(ctx.channel());
        //用来区分用户
        channelsMap.put(new User(100,"张三"), ctx.channel());
        channelGroup.writeAndFlush("[客户端]" + ctx.channel().remoteAddress() + "加入聊天\n");
    }

    /**
     * 用来提示上线通知,表示channel处于活动的状态,提示 xxxx 上线
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "上线了...\n");
    }

    /**
     * 当Channel处于非活动状态,提示xxx下线了
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "离线了...");
    }

    /**
     * 断开链接会被出发,将xx客户离开信息推送给当前在线的用户
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + "离线了\n");
        System.out.println("channelGroup size "+channelGroup.size());
    }

    /**
     * 处理逻辑
     * @param s
     * @throws Exception
     */
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {

        Channel channel = ctx.channel();
        channelGroup.forEach(ch->{
            //不是当前的channel
            if (ch != channel){
                ch.writeAndFlush("[客户]" + channel.remoteAddress() + "发送了消息:" + s + "\n");

            }else{
                ch.writeAndFlush("[自己]发送了消息" + s + "\n");
            }
        });
    }
}

Client端代码如下:

package com.netty.groupchat;

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.net.InetSocketAddress;
import java.util.Scanner;

public class GroupChatClient {

    private String host;
    private int port;

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


    public void run(){

        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();

        try{
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast("decoder", new StringDecoder());
                            socketChannel.pipeline().addLast("encoder", new StringEncoder());
                            socketChannel.pipeline().addLast(new GroupChatClientHandler());
                        }
                    });
            ChannelFuture sync = bootstrap.connect(new InetSocketAddress(host, port)).sync();
            System.out.println("------"+sync.channel().remoteAddress()+"------");
//            sync.channel().closeFuture().sync();
            //客户端要是收入信息
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String msg = scanner.nextLine();
                sync.channel().writeAndFlush(msg+"\n");
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            eventLoopGroup.shutdownGracefully();
        }


    }

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

class GroupChatClientHandler extends SimpleChannelInboundHandler<String>{

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(s.trim());
    }
}

心跳检测server端代码,可以用上面的client代码来进行测试哦

package com.netty.xtjc;

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.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

import static io.netty.handler.timeout.IdleState.*;
import static io.netty.handler.timeout.IdleState.READER_IDLE;

/**
 * 心跳检测
 */
public class MyServer {

    public static void main(String[] args) {

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

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(boosGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //加入一个Neety 提供的IdleStatehandler
                            /**
                             * 1 readerIdleTime 表示多长时间没有读,就会发送一个心跳检测包,检测是否有链接
                             * 2 writerIdletime 表示多长时间没有写
                             * 3 allIdleTime 表示多长时间没有读也没有写
                             * 当IdleStateEvent触发后,就会传给管道的下一个Handler去处理,通过调用(回调)的 userEventTriggerd
                             */
                            pipeline.addLast(new IdleStateHandler(3, 5, 7, TimeUnit.SECONDS));
                            //加入一个对空闲检测进一步处理自定义的Handler
                            pipeline.addLast(new MyServerHandler());
                        }
                    });

        ChannelFuture cf = null;
        try {
            cf = serverBootstrap.bind(new InetSocketAddress(6677)).sync();
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

}


class MyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        IdleStateEvent event = (IdleStateEvent) evt;
        String eventType = null;
        switch (event.state()){
            case READER_IDLE:
             eventType = "读空闲";
             break;
            case WRITER_IDLE:
                eventType = "写空闲";
                break;
            case ALL_IDLE:
                eventType = "读写空闲";
                break;
        }
        System.out.println(ctx.channel().remoteAddress() + "---超时时间---"+eventType);
//        ctx.channel().close();
    }
}

以下为聊天室的全部代码,目前实现的有群聊功能,并没有实现单聊功能,如果有兴趣的伙伴可以私聊哦,有问题在下面评论,技术问题可以私聊我。

发布了106 篇原创文章 · 获赞 101 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/qq_24434671/article/details/103343141
今日推荐