一、客戶端代碼及關鍵類說明
/*** netty5的客戶端* @author -zhengzx-**/
public class ClientSocket {public static void main(String[] args) {//服務類Bootstrap bootstrap = new Bootstrap();//workerEventLoopGroup worker = new NioEventLoopGroup();try {//設置線程池bootstrap.group(worker);//設置socket工廠bootstrap.channel(NioSocketChannel.class);//設置管道bootstrap.handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) throws Exception {ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new ClientSocketHandler());}});ChannelFuture connect = bootstrap.connect("127.0.0.1", 10101);BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));while(true){System.out.println("請輸入:");String msg = bufferedReader.readLine();connect.channel().writeAndFlush(msg);}} catch (Exception e) {e.printStackTrace();} finally{worker.shutdownGracefully();}}
}
【1】EventLoopGroup
: 客服端需要指定EvnetLoopGroup
,Netty5
中實例為NioEventLoopGroup
:表示一個NIO
的EvnetLoopGroup
。
【2】ChannelType
: 指定 Channel 的類型,客戶端為NioSocketChannel
。在Netty
中,Channel
是一個Socket
的抽象,它為用戶提供了關于Socket
狀態(是否連接還是斷開) 以及對Socket
的讀寫等操作。每當 Netty 建立了一個連接后, 都會有一個對應的 Channel
實例。
【3】Handler
: 設置數據的處理類。
public class ClientSocketHandler extends SimpleChannelInboundHandler<String>{@Overrideprotected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println("客戶端接受消息:"+msg);}
}
【4】ChannelPipeline
: 在實例化一個Channel
時,必然伴隨著實例化一個ChannelPipeline
。
二、服務端代碼及說明
【1】EventLoopGroup
: 不論是服務器端還是客戶端,都必須指定EventLoopGroup
. 在這個例子中, 指定了NioEventLoopGroup
, 表示一個NIO
的EventLoopGroup
, 不過服務器端需要指定兩個EventLoopGroup
, 一個是bossGroup
, 用于處理客戶端的連接請求; 另一個是workerGroup
, 用于處理與各個客戶端連接的IO
操作。
【2】ChannelType
: 指定Channel
的類型. 因為是服務器端, 因此使用了NioServerSocketChannel
。
【3】Handler
: 設置數據的處理器。
public class ServerSocketHandler extends SimpleChannelInboundHandler<String>{@Overrideprotected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println(msg);//返回字符串ctx.writeAndFlush("hi");}}
三、Netty5 與 Netty4/Netty3的區別
Netty4
和Netty5
的主要區別在于它們的版本更新以及一些新特性的添加。Netty5
是對Netty3
的后續版本,因此在架構上進行了一些重大調整,增加了許多新的功能和特性,使得其復雜性相較于Netty3
有所增加。然而,Netty4
與Netty5
的設計方式相似,因此它們之間的差異并不是特別大。簡而言之,Netty5
提供了比Netty3
更多的高級功能和新特性,而Netty4
不再作為單獨的版本維護,因為它與Netty5
的設計和實現相類似。
netty5
的復雜性相對于netty3
要多一些。架構基本被重構了。所以這里主要是介紹一些屬性和用法。
核心的變化主要有:
【1】支持Android
,使得移動設備變的更加強大;
【2】通過Ice Cream Sandwich
解決了在ADK
中最著名的與NIO
和SSLEngine
相關的問題,且用戶顯然想要重用他們應用中的的編解碼和處理器代碼;
【3】我們決定官方支持Android
(4.0
及以上版本)
簡化處理器層次:
【1】ChannelInboundHandler
和ChannelOutboundHandler
整合為ChannelHandler
。ChannelHandler
現在包含輸入和輸出的處理方法。
【2】ChannelInboundHandlerAdapter
,ChannelOutboundHandlerAdapter
和ChannelDuplexHandlerAdapter
已被廢棄,由ChannelHandlerAdapter
代替。
【3】由于現在無法區分處理器handler)
是輸入還是輸出的處理器,CombinedChannelDuplexHandler
現在由ChannelHandlerAppender
代替。
Channel.deregister()
已被移除。不再生效和被使用。取而代之的,我們將允許Channel
被充注冊到不同的事件循環。
ChannelHandlerContext.attr(..) == Channel.attr(..)
Channel和ChannelHandlerContext
類都實現了AttributeMap
接口,使用戶可以在其上關聯一個或多個屬性。有時會讓用戶感到困惑的是Channel
和ChannelHandlerContext
都有其自己的存儲用戶定義屬性的容器。例如,即使你通過Channel.attr(KEY_X).set(valueX)
給屬性'KEY_X’
賦值,你卻無法通過ChannelHandlerContext.attr(KEY_X).get()
方法獲取到值。反之亦是如此。這種行為不僅僅令人不解而且還浪費內存。
為了解決這個問題,我們決定每個Channel
內部僅保留一個map
。AttributeMap
總是用AttributeKey
作為它的key
。AttributeKey
確保鍵的唯一性,因此每個Channel
中如果存在一個以上的屬性容易是多余的。只要用戶把他自己的AttributeKey
定義成ChannelHandler
的private static final
變量,就不會有出現重復key
的風險。
更簡單更精確的緩沖區泄漏追蹤: 之前,查找緩沖區泄漏是很困難的,并且泄漏的警告信息也不是很有幫助。現在我們有了增強的泄漏報告機制,該機制會在增長超過上限時觸發。
PooledByteBufAllocator
成為默認的allocator
: 在4.x
版本中UnpooledByteBufAllocator
是默認的allocator
,盡管其存在某些限制。現在PooledByteBufAllocator
已經廣泛使用一段時間,并且我們有了增強的緩沖區泄漏追蹤機制,所以是時候讓PooledByteBufAllocator
成為默認了。
全局唯一的Channel ID
: 每個Channel
現在有了全局唯一的ID
,其生成的依據是:
MAC
地址(EUI-48
或是EUI-64
),最好是全局唯一的進程ID
;System#currentTimeMillis()
;System#nanoTime()
;- 隨機的
32
位整數,以及系列遞增的32
位整數;
可通過Channel.id()
方法獲取Channel
的ID
。
更靈活的線程模型: 增加了新的ChannelHandlerInvoker
接口,用于使用戶可以選擇使用哪個線程調用事件處理方法。替代之前的在向ChannelPipeline
添加ChannelHandler
時指定一個EventExecutor
的方式,使用該特性需要指定一個用戶自定義的ChannelHandlerInvoker
實現。
EmbeddedChannel
的易用性: EmbeddedChannel
中的readInbound()
和readOutbound()
方法返回專門類型的參數,因此你不必在轉換他們的返回值。這可以簡化你的測試用例代碼。
EmbeddedChannel ch = ...;// BEFORE:
FullHttpRequest req = (FullHttpRequest) ch.readInbound();// AFTER:
FullHttpRequest req = ch.readInbound();
使用Executor
代替ThreadFactory
: 有些應用要求用戶使用Executor
運行他們的任務。4.x
版本要求用戶在創建事件循環event loop
時指定ThreadFacotry
,現在不再是這樣了。
Class loader
友好化: 一些類型,如AttributeKey
對于在容器環境下運行的應用是不友好的,現在不是了。
編解碼和處理器handlers
:
XmlFrameDecoder
支持流式的XML
文檔;- 二進制的
memcache
協議編解碼; - 支持
SPDY/3.1
(也移植到了4.x
版本); - 重構了
HTTP
多部分的編解碼;