1. 程式人生 > >java使用Netty實現點對點聊天

java使用Netty實現點對點聊天

最近學習伺服器開放,實現客戶端(APP)與硬體裝置之間的通訊,我一想到socket,經過查詢資料發現socket實現起來非常麻煩,同時也發現一個比較好的框架netty,覺得很不錯,就開始嘗試用他來擼一個點對點聊天系統,想了解的小夥伴可以自行去學習一下netty
一、一開始是導包,我是匯入這三個包
在這裡插入圖片描述

二、開啟伺服器,話不多說直接上程式碼,比較程式碼很簡單
DiscardServer.java 主函式開啟服務

public class DiscardServer implements Runnable{

    private int port;

    private DiscardServer(int port) {
        this.port = port;
    }

    public void run()  {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // (3)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DiscardServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            Channel f = b.bind(port).sync().channel(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 1001;
        }

       new Thread( new DiscardServer(port)).start();
    }
}

DiscardServerHandler.java 負責通訊和訊息處理

public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)

        ByteBuf in = (ByteBuf) msg;
        System.out.print(in.toString(io.netty.util.CharsetUtil.US_ASCII));
        // 以靜默方式丟棄接收的資料
      //  ((ByteBuf) msg).release(); // (3)
//        ctx.writeAndFlush(firstMSG);
     //   String name=in.toString(io.netty.util.CharsetUtil.US_ASCII);
        ApplicationContext.dealMessage(in.toString(io.netty.util.CharsetUtil.US_ASCII),ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ApplicationContext.writeMessage("ni hao a",ctx);
        System.out.println("this"+ctx.channel().remoteAddress().toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // 出現異常時關閉連線。
        cause.printStackTrace();
        ctx.close();
    }

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

ApplicationContext.java 負責客戶端儲存,訊息協議制度和處理,定義Map來儲存使用者ID和客戶端

public class ApplicationContext {
    private static Map<Integer, ChannelHandlerContext> onlineUsers = new HashMap<Integer, ChannelHandlerContext>();//儲存使用者客戶端訊息

    private static void add(Integer uid, ChannelHandlerContext ctx) {
        onlineUsers.put(uid, ctx);
    }

 

    public static void remove(Integer uid) {
        onlineUsers.remove(uid);
    }

    private static ChannelHandlerContext getContext(Integer uid) {
        return onlineUsers.get(uid);
    }

    /**
     * 訊息處理、自定義協議
     * @param message 訊息
     * @param ctx 客戶端
     */
    static void dealMessage(String message, ChannelHandlerContext ctx) {
        String[] strings = message.split("#");
        System.out.println(strings.length+" length");
        //定義協議#號隔開陣列下標0:資料型別、1:接收端使用者ID、2:傳送端ID、3內容
        if (strings.length != 4)
            return;
        switch (strings[0]) {
            case "0"://認證客戶端
                add(Integer.valueOf(strings[2]), ctx);
                break;

            case "2"://指定使用者傳送
                ChannelHandlerContext ctxTwo = getContext(Integer.valueOf(strings[1]));
                if (ctxTwo != null)
                    writeMessage(message,ctxTwo);
                else
                    writeMessage("is get out\n",ctx);
                    break;
        }
    }

    /**
     * 傳送訊息
     * @param message 訊息
     * @param ctx 客戶端
     */
    static void writeMessage(String message, ChannelHandlerContext ctx) {
        ctx.writeAndFlush(Unpooled.buffer(message.getBytes().length).writeBytes(message.getBytes()));
    }
}

接下來就大功告成了

三、除錯方式
連線伺服器,收到伺服器返回(ni hao a)的訊息, 馬上返回訊息繫結客戶端如0#1#2#3,及表示自己客戶端的id為2,其他客戶端可通過id來向自己傳送訊息,傳送訊息給其他客戶端如2#1#2#4,給id為1的客戶端傳送訊息4。
上面那個只是我簡單設定的一個協議,大家使用可以的話可以設定其他的協議或者資料格式json、XML等等