Java Netty 學習(七) - 第一個Netty程式
阿新 • • 發佈:2018-11-01
介紹
Netty是由JBOSS提供的一個java開源框架。Netty提供非同步的、事件驅動的網路應用程式框架和工具,它簡化了程式設計師的工作,用以快速開發高效能、高可靠性的網路伺服器和客戶端程式。
本系列文章講慢慢一起走進學習Netty
本篇文章就以一個Hello Word程式開始。
例子
首先,Netty作為網路程式設計的框架,自然離不開Socket,同時,也包括Server端以及Client端。他們利用Socket進行資訊傳輸。
Socket可以理解為應用層與TCP/IP協議族通訊的中間軟體抽象層。
關於Socket可看文章:Socket
下面看demo
Client端
首先是一個啟動的類:
public class Client { private Integer port; private String address; public Client(Integer port, String address){ this.port = port; this.address = address; } /** * 用於連線伺服器 */ public void start() throws Exception{ EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HelloClientHandler()); } }); System.out.println("begin to connect..."); ChannelFuture future = b.connect(this.address, this.port); future.channel().closeFuture().sync(); } public static void main(String[] args) throws Exception { Client client = new Client(8989, "127.0.0.1"); client.start(); } }
再看看裡面的HelloClientHandler
public class HelloClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { System.out.println("Client received Message from server: " + msg.toString(CharsetUtil.UTF_8)); //讀取資料就輸出 } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8)); //活躍時候輸出 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); //遇到錯誤就關閉 } }
對於上面的client端的程式碼,可以看到下面幾個概念:
EventLoopGroup
:可以理解為執行緒池,裡面產生EventLoop
來執行channel
所有產生的事件Bootstrap
:啟動器,講Netty
各個元件串起來使之執行Pipline
:可以理解為管道,或者流水線,裡面可以裝載多個ChannelHandler並讓他們執行ChannelHandler
:例如HelloClientHandler
,具體的channel
所處理的工作
整個過程可以理解為這樣一個過程,分發快遞過程,快遞車把快遞送到目的地,此時有一個或者多個快遞員(EventLoop)進行快遞分發,分發到給不同的區域標籤(Pipline),此時,由於快遞是已經定義好的,再有快遞員發給不同的快遞手上(執行ChannelHandler所定一個的規則)
EventLoop 的作用是一個死迴圈,而這個迴圈中做3件事情:
- 有條件的等待 NIO 事件
- 處理 NIO 事件
- 處理訊息佇列中的任務
下面看看服務端程式碼
Server端
首先也是一個啟動類:
public class Server {
private Integer port;
public Server(Integer port){
this.port = port;
}
public void startServer() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println("new channel");
ch.pipeline().addLast(new HelloServerHandler());
}
});
System.out.println("begin to run server");
ChannelFuture future = serverBootstrap.bind().sync();
future.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
Server server = new Server(8989);
server.startServer();
}
}
再看HelloServerHandler,和前面的HelloClientHandler邏輯差不多:
public class HelloServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received from client: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in); //傳送出去,所以client端可以讀
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server active");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println(cause);
ctx.close();
}
}
就看啟動程式碼來看,服務端程式碼和客戶端程式碼還是很相似的
Bootstrap
變為ServerBootstrap
NioSocketChannel
變為NioServerSocketChannel
- 而在Handler處理上,client端的
handler
變為了childHandler
整個例子的流程如下:
- 服務端啟動,監聽127.0.0.1:8989埠
- 客戶端連線埠,服務端監聽到,輸出
new channel
並把HelloServerHandler
加入到pipline
中 - 此時,客戶端連線上後,觸發了
channelActive
事件,並把Netty rocks!
發了出去 - 此時,服務端接受到了資料
Netty rocks!
,觸發了HelloServerHandler
中事件,所以列印了Server received: Netty rocks!
,並且傳送了出去 - 此時,由於客戶端接受訊息,觸發了
channelRead0
所以最後列印了Client received: Netty rocks!
整個例子就完成了。
資料:
- Netty In Action