1. 程式人生 > >Netty4(二)服務端和客戶端實現

Netty4(二)服務端和客戶端實現

目標

用netty4實現一個服務端和客戶端,兩者之間可以進行測試通訊

程式碼UML類圖

服務端

server.png

客戶端

singleClient.png

Netty4實現服務端

服務類

package com.mym.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import
io.netty.handler.codec.string.StringEncoder; /** * netty服務端 */ public class NettyServer { public static void main(String[] args) { startServer(); } public static void startServer(){ //1.定義server啟動類 ServerBootstrap serverBootstrap = new ServerBootstrap(); //2.定義工作組:boss分發請求給各個worker:boss負責監聽埠請求,worker負責處理請求(讀寫)
EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); //3.定義工作組 serverBootstrap.group(boss,worker); //4.設定通道channel serverBootstrap.channel(NioServerSocketChannel.class);//A //serverBootstrap.channelFactory(new ReflectiveChannelFactory(NioServerSocketChannel.class));//舊版本的寫法,但是此過程在A中有同樣過程
//5.新增handler,管道中的處理器,通過ChannelInitializer來構造 serverBootstrap.childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) throws Exception { //此方法每次客戶端連線都會呼叫,是為通道初始化的方法 //獲得通道channel中的管道鏈(執行鏈、handler鏈) ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new StringDecoder()); pipeline.addLast("serverHandler1",new ServerHandler()); pipeline.addLast("serverHandler2",new ServerHandler2()); pipeline.addLast(new StringEncoder()); System.out.println("success to initHandler!"); } }); //6.設定引數 //設定引數,TCP引數 serverBootstrap.option(ChannelOption.SO_BACKLOG, 2048); //連線緩衝池的大小 serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//維持連結的活躍,清除死連結 serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);//關閉延遲傳送 //7.繫結ip和port try { ChannelFuture channelFuture = serverBootstrap.bind("0.0.0.0", 9099).sync();//Future模式的channel物件 //7.5.監聽關閉 channelFuture.channel().closeFuture().sync(); //等待服務關閉,關閉後應該釋放資源 } catch (InterruptedException e) { System.out.println("server start got exception!"); e.printStackTrace(); }finally { //8.優雅的關閉資源 boss.shutdownGracefully(); worker.shutdownGracefully(); } } }

Handler1

package com.mym.netty.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ServerHandler extends ChannelInboundHandlerAdapter {


    /*
    * ChannelInboundHandlerAdapter:ChannelInboundHandlerAdapter是ChannelInboundHandler的一個簡單實現,預設情況下不會做任何處理,
    *   只是簡單的將操作通過fire*方法傳遞到ChannelPipeline中的下一個ChannelHandler中讓鏈中的下一個ChannelHandler去處理。
    *
    * SimpleChannelInboundHandler:SimpleChannelInboundHandler支援泛型的訊息處理,預設情況下訊息處理完將會被自動釋放,無法提供
    *   fire*方法傳遞給ChannelPipeline中的下一個ChannelHandler,如果想要傳遞給下一個ChannelHandler需要呼叫ReferenceCountUtil#retain方法。
    * */

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("ServerHandler receive msg:"+msg.toString());

        //寫訊息:先得到channel,在寫如通道然後flush重新整理通道把訊息發出去。
        ctx.channel().writeAndFlush("this is ServerHandler reply msg happend at !"+System.currentTimeMillis());

        //把訊息往下一個Handler傳
        ctx.fireChannelRead(msg);
    }
}

Handler2

package com.mym.netty.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ServerHandler2  extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("ServerHandler2 receive msg:"+msg.toString());
        ctx.channel().writeAndFlush("this is ServerHandler2 reply msg happend at !"+System.currentTimeMillis());
    }
}

Netty4實現客戶端

客戶端服務類

package com.mym.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

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

/**
 * netty客戶端
 */
public class NettySingleClient {

    public static void main(String[] args) {
        startClient();
    }

    public static void startClient(){
        //1.定義服務類
        Bootstrap clientBootstap = new Bootstrap();

        //2.定義執行執行緒組
        EventLoopGroup worker = new NioEventLoopGroup();

        //3.設定執行緒池
        clientBootstap.group(worker);

        //4.設定通道
        clientBootstap.channel(NioSocketChannel.class);

        //5.新增Handler
        clientBootstap.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                System.out.println("client channel init!");
                ChannelPipeline pipeline = channel.pipeline();
                pipeline.addLast("StringDecoder",new StringDecoder());
                pipeline.addLast("StringEncoder",new StringEncoder());
                pipeline.addLast("ClientHandler",new ClientHandler());
            }
        });

        //6.建立連線
        ChannelFuture channelFuture = clientBootstap.connect("0.0.0.0",9099);
        try {
            //7.測試輸入
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            while(true){
                System.out.println("請輸入:");
                String msg = bufferedReader.readLine();
                channelFuture.channel().writeAndFlush(msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //8.關閉連線
            worker.shutdownGracefully();
        }
    }
}

客戶端的handler

package com.mym.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("client receive msg:"+msg.toString());
    }
}

測試

啟動服務端和客戶端後,客戶端傳送nihao!服務端迴應,然後客戶端傳送hello,服務端迴應。
服務端輸出

success to initHandler!
ServerHandler receive msg:nihao!
ServerHandler2 receive msg:nihao!
ServerHandler receive msg:hello
ServerHandler2 receive msg:hello

客戶端輸出

client channel init!
請輸入:
nihao!
請輸入:
client receive msg:this is ServerHandler reply msg happend at !1531893027697this is ServerHandler2 reply msg happend at !1531893027698
hello
請輸入:
client receive msg:this is ServerHandler reply msg happend at !1531893045446this is ServerHandler2 reply msg happend at !1531893045447

小結

需要注意的是,服務端和客戶端除了啟動類和socket channel不一樣以外,其他幾乎一致的操作。

本文的客戶端是單連線,下文將介紹多連線客戶端的操作。