【Netty入門】基於Netty的Server / Client
回顧NIO
我在之前介紹了NIO的server與client,雖然說程式設計相對於BIO比較複雜,但是效能高啊,而且Netty的server/client也是基於NIO的。所以,在介紹Netty的server/client 之前,先回顧一下NIO 的server / Client的步驟還是很有必要的!
1.NIO 服務端Server開發步驟:
(1)建立ServerSocketChannel,配置它為非阻塞模式;
(2)繫結監聽,配置TCP引數,例如:backlog大小;
(3)建立一個獨立的I/O執行緒,用於輪詢多路複用器Selector;
(4)建立Selector,將之前建立的ServerSocketChannel註冊到Selector上,監聽SelectorKey.ACCEPT;
(5)啟動I/O,在迴圈體中執行Selector.select() 方法,輪詢就緒的Channel;
(6)當輪詢到了處於就緒狀態的Channel時,需要對其進行判斷,如果是OP_ACCEPT狀態,說明是新的客戶端接入,則呼叫ServerSocketChannel.accept() 方法接受新的客戶端;
(7)設定新接入的客戶端鏈路SocketChannel為非阻塞模式,配置其他的一些TCP引數;
(8)將SocketChannel註冊到Selector,監聽OP_READ操作位;
(9)如果輪詢的Channel為OP_READ,則說明SocketChannel中有新的就緒的資料包需要讀取,則構造ByteBuffer物件,讀取資料包;
(10)如果輪詢的Channel為OP_WRITE,說明還有資料沒有傳送完成,需要繼續傳送。
2.NIO 客戶端client開發步驟:
(1)開啟SocketChannel,配置它為非阻塞模式,同時設定TCP引數
(2)非同步連線到服務端,若連線成功,就向多路複用器註冊讀事件OP_READ,否則註冊OP_CONNECT事件,請求再次連線。
(3)建立Selector,啟動執行緒;
(4)Selector輪詢就緒的SelectionKey
(5)若SelectionKey是連線事件,則判斷連線是否成功,若連線成功,向多路複用器註冊讀事件OP_READ;
(6)若SelectionKey是可讀事件,就建立ByteBuffer,用於讀取資料;
(7)對ByteBuffer進行編解碼;
(8)非同步寫ByteBuffer到SocketChannel。
小結:
你是不是感到“巨複雜”,步驟怎麼這麼多,直接使用NIO程式設計的步驟的確很繁瑣,所以,Netty應運而生,它簡化了步驟,更易上手,我將在接下來介紹Netty的Server / Client程式。
版本 :基於Netty 4.1.11
Netty的服務端程式碼
public class Server4 {
public static void main(String[] args) throws SigarException {
//boss執行緒監聽埠,worker執行緒負責資料讀寫
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try{
//輔助啟動類
ServerBootstrap bootstrap = new ServerBootstrap();
//設定執行緒池
bootstrap.group(boss,worker);
//設定socket工廠
bootstrap.channel(NioServerSocketChannel.class);
//設定管道工廠
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//獲取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//字串解碼器
pipeline.addLast(new StringDecoder());
//字串編碼器
pipeline.addLast(new StringEncoder());
//處理類
pipeline.addLast(new ServerHandler4());
}
});
//設定TCP引數
//1.連結緩衝池的大小(ServerSocketChannel的設定)
bootstrap.option(ChannelOption.SO_BACKLOG,1024);
//維持連結的活躍,清除死連結(SocketChannel的設定)
bootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);
//關閉延遲傳送
bootstrap.childOption(ChannelOption.TCP_NODELAY,true);
//繫結埠
ChannelFuture future = bootstrap.bind(8866).sync();
System.out.println("server start ...... ");
//等待服務端監聽埠關閉
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//優雅退出,釋放執行緒池資源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
class ServerHandler4 extends SimpleChannelInboundHandler<String> {
//讀取客戶端傳送的資料
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("client response :"+msg);
ctx.channel().writeAndFlush("i am server !");
// ctx.writeAndFlush("i am server !").addListener(ChannelFutureListener.CLOSE);
}
//新客戶端接入
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
//客戶端斷開
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//異常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//關閉通道
ctx.channel().close();
//列印異常
cause.printStackTrace();
}
}
Netty的客戶端程式碼
public class Client4 {
public static void main(String[] args) {
//worker負責讀寫資料
EventLoopGroup worker = new NioEventLoopGroup();
try {
//輔助啟動類
Bootstrap bootstrap = new Bootstrap();
//設定執行緒池
bootstrap.group(worker);
//設定socket工廠
bootstrap.channel(NioSocketChannel.class);
//設定管道
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//獲取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//字串解碼器
pipeline.addLast(new StringDecoder());
//字串編碼器
pipeline.addLast(new StringEncoder());
//處理類
pipeline.addLast(new ClientHandler4());
}
});
//發起非同步連線操作
ChannelFuture futrue = bootstrap.connect(new InetSocketAddress("127.0.0.1",8866)).sync();
//等待客戶端鏈路關閉
futrue.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//優雅的退出,釋放NIO執行緒組
worker.shutdownGracefully();
}
}
}
class ClientHandler4 extends SimpleChannelInboundHandler<String> {
//接受服務端發來的訊息
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("server response : "+msg);
}
//與伺服器建立連線
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//給伺服器發訊息
ctx.channel().writeAndFlush("i am client !");
System.out.println("channelActive");
}
//與伺服器斷開連線
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//異常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//關閉管道
ctx.channel().close();
//列印異常資訊
cause.printStackTrace();
}
}
服務端執行結果:
客戶端執行結果:
注:我使用的Netty的版本為Netty 4.1.11,Netty各個版本之間的程式碼或有些許差異,請注意。
小結:可以明確感覺到Netty的伺服器客戶端程式明顯看起來順眼了不少,當然,它比NIO用起來順手多了。同時,Netty的效能也是很高的。
本人才疏學淺,若有錯誤,請指出
謝謝!