1.使用Netty能夠做什麼
開發異步、非阻塞的TCP網絡應用程式;
開發異步、非阻塞的UDP網絡應用程式;
開發異步檔案傳輸應用程式;
開發異步HTTP服務端和用戶端應用程式;
提供對多種編解碼架構的內建,包括谷歌的Protobuf、Jboss marshalling、Java序列化、壓縮編解碼、XML解碼、字元串編解碼等,這些編解碼架構可以被使用者直接使用;
提供形式多樣的編解碼基礎類庫,可以非常友善的實作私有協定棧編解碼架構的二次定制和開發;
基于職責鍊模式的Pipeline-Handler機制,使用者可以非常友善的對網絡事件進行攔截和定制;
所有的IO操作都是異步的,使用者可以通過Future-Listener機制主動Get結果或者由IO線程操作完成之後主動Notify結果,使用者的業務線程不需要同步等待;
IP黑白名單控制;
列印消息碼流;
流量控制和整形;
性能統計;
基于鍊路空閑事件檢測的心跳檢測
2. Netty常用類講解
在這裡我們就一些我們常用到的類做大緻的講解,然後再寫入門程式的時候大緻知道每一行都講了什麼。
EventLoop,EventLoopGroup
EventLoop目的是為Channel處理IO操作,一個EventLoop可以為多個Channel服務,EventLoopGroup會包含多個EventLoop。
BootStrap,ServerBootstrap
一個Netty應用通常由一個Bootstrap開始,它主要作用是配置整個Netty程式,串聯起各個元件。
ChannelInitializer
當一個連結建立時,我們需要知道怎麼來接收或者發送資料,當然,我們有各種各樣的Handler實作來處理它,那麼ChannelInitializer便是用來配置這些Handler,它會提供一個ChannelPipeline,并把Handler加入到ChannelPipeline。
Handler
為了支援各種協定和處理資料的方式,便誕生了Handler元件。Handler主要用來處理各種事件,這裡的事件很廣泛,比如可以是連接配接、資料接收、異常、資料轉換等。
ChannelInboundHandler
一個最常用的Handler。這個Handler的作用就是處理接收到資料時的事件,也就是說,我們的業務邏輯一般就是寫在這個Handler裡面的,ChannelInboundHandler就是用來處理我們的核心業務邏輯。
Future
在Netty中所有的IO操作都是異步的,是以,你不能立刻得知消息是否被正确處理,但是我們可以過一會等它執行完成或者直接注冊一個監聽,具體的實作就是通過Future和ChannelFutures,他們可以注冊一個監聽,當操作執行成功或失敗時監聽會自動觸發。總之,所有的操作都會傳回一個ChannelFuture。
3. 第一個Helloworld
上面我們已經對常用類進行說明,下面我們就使用這些類來建構我們的第一個入門程式,本示例我使用的是maven來建構工程,如果你使用的是普通的項目則跳過第一步。
首先引入maven jar包:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.5.Final</version>
</dependency>
下面我們來寫用戶端:
public class HelloWorldClient {
private int port;
private String address;
public HelloWorldClient(int port,String address) {
this.port = port;
this.address = address;
}
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {
Channel channel = bootstrap.connect(address,port).sync().channel();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for(;;){
String msg = reader.readLine();
if(msg == null){
continue;
}
channel.writeAndFlush(msg + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWorldClient client = new HelloWorldClient(7788,"127.0.0.1");
client.start();
}
}
ChannelInitializer用來配置處理資料的handler:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/*
* 這個地方的 必須和服務端對應上。否則無法正常解碼和編碼
*
* 解碼和編碼 我将會在下一節為大家詳細的講解。暫時不做詳細的描述
*
* /
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 我們自己的handler
pipeline.addLast("handler", new HelloWorldClientHandler());
}
}
public class HelloWordServer {
private int port;
public HelloWordServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWordServer server = new HelloWordServer(7788);
server.start();
}
}
服務端的ChannelInitializer:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 字元串解碼 和 編碼
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 自己的邏輯Handler
pipeline.addLast("handler", new HelloWordServerHandler());
}
}
伺服器端的handler:
public class HelloWordServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
ctx.write("received your msg");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
上面伺服器端和用戶端的代碼都已經寫完,下面我們先啟動服務端,然後啟動用戶端,程式中我是在用戶端讓手動輸入,輸入結束之後回車,伺服器端即可接受資料。
用戶端:
服務端: