netty服務端詳解

本文僅適用與Netty4.0.32版本,其餘版本是否適用表示並不清楚...算法

Netty服務器啓動流程:

一、建立線程池bootstrap

建立處理鏈接的線程池:bossGroup
建立處理全部事件的線程池:workerGroup緩存

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

二、設定輔助啓動類。ServerBootStrap
傳入1中開闢的線程池
指定鏈接該服務器的channel類型
指定須要執行的childHandler
設置部分參數,如AdaptiveRecvByteBufAllocator緩存大小
.Option用於設置bossGroup相關參數
.childOption用於設置workerGroup相關參數
2.五、此處可處理一個問題:超長字符串在服務端handler沒法被一次接收完
可經過此句進行設置:.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, MAX_LENGTH_OF_MSG, 65536))服務器

ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)//設置channel類型
    .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, MAX_LENGTH_OF_MSG, 65536))
    .childHandler(new childChannelHandler());//選擇執行handler

此處的MAX_LENGTH_OF_MSG必須爲2的次冪,否則確定不會是你設置的那個值,具體會變成什麼,源碼還沒看,等看了再補充...網絡

2.7五、構建Handler處理流程socket

  樣例以下:tcp

public class childChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            //TODO 添加各類功能handler 消息加解密,消息規範檢測,構建返回碼
            ch.pipeline().addLast(new NettyServerHandler());
        }
    }

  當要添加多個handler時,就必須注意添加的順序。ide

  這裏的handler分爲兩種類型:函數

    一種繼承ChannelInboundHandler,用於處理來自客戶端的消息,好比對客戶端的消息進行解碼,讀取等等。該類型在pipeline中的執行順序與添加順序一致。oop

    一種繼承ChannelOutboundHandler,用於處理即將發往客戶端的消息,好比對該消息進行編輯,編碼等等。該類型在pipeline中的執行順序與添加順序相反。

  並且ChannelOutboundHandler的全部handler,放在ChannelInboundHandler下面是執行不到的。

好比:

public class childChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        public void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new OutboundHandler1());  //handler1
            ch.pipeline().addLast(new OutboundHandler2());  //handler2
            ch.pipeline().addLast(new InboundHandler1());   //handler3
            ch.pipeline().addLast(new InboundHandler2());   //handler4
        }
    }

  以上4個handler的實際執行順序分別爲handler3 -> handler4 -> handler2 ->handler1

  若是在handler4下方加上OutboundHandler3,那麼這個handler是不會被執行到的。
三、同步等待綁定指定端口
此處可屢次執行bind語句綁定多個端口

ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
    channelFuture = serverBootstrap.bind(8081).sync();
    ...

四、同步等待服務器關閉信息

  channelFuture.channel().closeFuture().sync();

五、最後關閉此前開闢的兩個線程池

  bossGroup.shutdownGracefully();
  workerGroup.shutdownGracefully();

最後整段服務器代碼以下:

package Netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.AdaptiveRecvByteBufAllocator;


public class NettyServer {
    public void startServerInPort(int port) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            //設置啓動輔助類
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)//設置channel類型
            .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 2048, 65536))
            .childHandler(new childChannelHandler());//選擇執行handler
            
            //阻塞等待服務器徹底啓動
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            
            channelFuture.channel().closeFuture().sync();
        }finally{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    public class childChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            //TODO 添加各類功能handler 消息加解密,消息規範檢測,構建返回碼
            ch.pipeline().addLast(new NettyServerHandler());
        }
    }
}

 客戶端的這部分代碼和服務器端差很少,就不另開一文囉嗦了。之間貼代碼:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class NettyClient {
    
    public void sendMsgToServer() throws Exception{
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            //設置輔助啓動類信息
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
            .channel(NioSocketChannel.class)//選擇channel類型
            .option(ChannelOption.TCP_NODELAY, true)
            .handler(new childChannelHandler());
            
            //阻塞等待成功鏈接服務器
            ChannelFuture channelFuture = bootstrap.connect(localhost,8000).sync();
            
            //阻塞等待來自服務器的處理結果
            channelFuture.channel().closeFuture().sync();
        }finally{
            group.shutdownGracefully();
        }
    }
    
    private class childChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            //TODO 添加其餘功能處理Handler,如消息加解密
            ch.pipeline().addLast(new NettyClientHandler());
        }
    }

}

下面簡單的總結一下ChannelOption的含義已及使用的場景

ChannelOption.SO_BACKLOG

    ChannelOption.SO_BACKLOG對應的是tcp/ip協議listen函數中的backlog參數,函數listen(int socketfd,int backlog)用來初始化服務端可鏈接隊列,

    服務端處理客戶端鏈接請求是順序處理的,因此同一時間只能處理一個客戶端鏈接,多個客戶端來的時候,服務端將不能處理的客戶端鏈接請求放在隊列中等待處理,backlog參數指定了隊列的大小

ChannelOption.SO_REUSEADDR

    ChanneOption.SO_REUSEADDR對應於套接字選項中的SO_REUSEADDR,這個參數表示容許重複使用本地地址和端口,

    好比,某個服務器進程佔用了TCP的80端口進行監聽,此時再次監聽該端口就會返回錯誤,使用該參數就能夠解決問題,該參數容許共用該端口,這個在服務器程序中比較常使用,

    好比某個進程非正常退出,該程序佔用的端口可能要被佔用一段時間才能容許其餘進程使用,並且程序死掉之後,內核一須要必定的時間纔可以釋放此端口,不設置SO_REUSEADDR

    就沒法正常使用該端口。

ChannelOption.SO_KEEPALIVE

    Channeloption.SO_KEEPALIVE參數對應於套接字選項中的SO_KEEPALIVE,該參數用於設置TCP鏈接,當設置該選項之後,鏈接會測試連接的狀態,這個選項用於可能長時間沒有數據交流的

    鏈接。當設置該選項之後,若是在兩小時內沒有數據的通訊時,TCP會自動發送一個活動探測數據報文。

ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF

    ChannelOption.SO_SNDBUF參數對應於套接字選項中的SO_SNDBUF,ChannelOption.SO_RCVBUF參數對應於套接字選項中的SO_RCVBUF這兩個參數用於操做接收緩衝區和發送緩衝區

    的大小,接收緩衝區用於保存網絡協議站內收到的數據,直到應用程序讀取成功,發送緩衝區用於保存發送數據,直到發送成功。 

ChannelOption.SO_LINGER

    ChannelOption.SO_LINGER參數對應於套接字選項中的SO_LINGER,Linux內核默認的處理方式是當用戶調用close()方法的時候,函數返回,在可能的狀況下,儘可能發送數據,不必定保證

    會發生剩餘的數據,形成了數據的不肯定性,使用SO_LINGER能夠阻塞close()的調用時間,直到數據徹底發送

ChannelOption.TCP_NODELAY

    ChannelOption.TCP_NODELAY參數對應於套接字選項中的TCP_NODELAY,該參數的使用與Nagle算法有關

    Nagle算法是將小的數據包組裝爲更大的幀而後進行發送,而不是輸入一次發送一次,所以在數據包不足的時候會等待其餘數據的到了,組裝成大的數據包進行發送,雖然該方式有效提升網絡的有效

    負載,可是卻形成了延時,而該參數的做用就是禁止使用Nagle算法,使用於小數據即時傳輸,於TCP_NODELAY相對應的是TCP_CORK,該選項是須要等到發送的數據量最大的時候,一次性發送

    數據,適用於文件傳輸。

相關文章
相關標籤/搜索