netty系列之:请netty再爱UDT一次
文章目錄
- 簡介
- netty對UDT的支持
- 搭建一個支持UDT的netty服務
- 異常來襲
- TypeUDT和KindUDT
- 構建ChannelFactory
- SelectorProviderUDT
- 使用UDT
- 總結
簡介
UDT是一個非常優(yōu)秀的協(xié)議,可以提供在UDP協(xié)議基礎上進行高速數據傳輸。但是可惜的是在netty 4.1.7中,UDT傳輸協(xié)議已經被標記為Deprecated了!
意味著在后面的netty版本中,你可能再也看不到UDT協(xié)議了.
優(yōu)秀的協(xié)議怎么能夠被埋沒,讓我們揭開UDT的面紗,展示其優(yōu)秀的特性,讓netty再愛UDT一次吧。
netty對UDT的支持
netty對UDT的支持體現在有一個專門的UDT包來處理UDT相關事情:package io.netty.channel.udt。
這個包里面主要定義了UDT的各種channel、channel配置、UDT消息和提供ChannelFactory和SelectorProvider的工具類NioUdtProvider.
搭建一個支持UDT的netty服務
按照netty的標準流程,現在是需要創(chuàng)建一個netty服務的時候了。
netty創(chuàng)建server服務無非就是創(chuàng)建EventLoop、創(chuàng)建ServerBootstrap、綁定EventLoop、指定channel類型就完了,非常的簡單。
唯一不同的就是具體的childHandler,可能根據具體協(xié)議的不同使用不同的處理方式。
當然,如果不是NioSocketChannel,那么對應的ChannelFactory和SelectorProvider也會有所變化。
沒關系,我們先看下如何創(chuàng)建支持UDT的netty服務:
final ThreadFactory acceptFactory = new DefaultThreadFactory("accept");final ThreadFactory connectFactory = new DefaultThreadFactory("connect");final NioEventLoopGroup acceptGroup = new NioEventLoopGroup(1, acceptFactory, NioUdtProvider.BYTE_PROVIDER);final NioEventLoopGroup connectGroup = new NioEventLoopGroup(1, connectFactory, NioUdtProvider.BYTE_PROVIDER);final ServerBootstrap boot = new ServerBootstrap();boot.group(acceptGroup, connectGroup).channelFactory(NioUdtProvider.BYTE_ACCEPTOR).option(ChannelOption.SO_BACKLOG, 10).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<UdtChannel>() {@Overridepublic void initChannel(final UdtChannel ch) {ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO),new UDTEchoServerHandler());}});// 開啟服務final ChannelFuture future = boot.bind(PORT).sync();可以看到,UDT和普通netty socket服務不同的地方在于它的selector和channelFactory都是由NioUdtProvider來提供了。
NioUdtProvider是netty核心包中的內容,他提供了對UDT的有用封裝,我們不需要要懂太多UDT內部的實現,就可以使用UDT協(xié)議,是不是很美妙。
異常來襲
如果有小伙伴興沖沖的拿上面這段代碼去嘗試運行,那么很可惜你會得到異常,異常大概類似下面的情況:
包com.barchart.udt找不到!什么?直接使用netty包中的類居然會報錯?是可忍孰不可忍!
我們來仔細分析一下,這里只有一個新的類就是NioUdtProvider,打開NioUdtProvider的源碼,在import一欄,我們赫然發(fā)現居然引用了不屬于netty的包,就是這些包報錯了:
import com.barchart.udt.SocketUDT; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.ChannelUDT; import com.barchart.udt.nio.KindUDT; import com.barchart.udt.nio.RendezvousChannelUDT; import com.barchart.udt.nio.SelectorProviderUDT;雖然很詭異,但是我們要想程序跑起來還是需要找出這些依賴包,經過本人的跋山涉水、翻山越嶺終于功夫不負苦心人,下面的依賴包找到了:
<dependency><groupId>com.barchart.udt</groupId><artifactId>barchart-udt-core</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.barchart.udt</groupId><artifactId>barchart-udt-bundle</artifactId><version>2.3.0</version></dependency>netty核心包居然要依賴與第三方庫,這可能就是netty準備刪除對UDT支持的原因吧。
TypeUDT和KindUDT
如果你去查看barchart中類的具體信息,就會發(fā)現這個包的作者有個癖好,喜歡把類后面帶上一個UDT。
當你看到滿屏的類都是以UDT結尾的時候,沒錯,它就是netty UDT依賴的包barchart本包了。
大牛們開發(fā)的包我們不能說他不好,只能說看起來有點累…
barchart包中有兩個比較核心的用來區(qū)分UDT type和kind的兩個類,分別叫做TypeUDT和KindUDT.
Type和kind翻譯成中文好像沒太大區(qū)別。但是兩者在UDT中還是有很大不同的。
TypeUDT表示的是UDT socket的模式。它有兩個值,分別是stream和datagram:
STREAM(1),DATAGRAM(2),表示數據傳輸是以字節(jié)流的形式還是以數據報文消息的格式來進行傳輸。
KindUDT則用來區(qū)分是服務器端還是客戶端,它有三種模式:
ACCEPTOR, CONNECTOR, RENDEZVOUSserver模式對應的是ACCEPTOR,用來監(jiān)聽和接收連接.它的代表是ServerSocketChannelUDT,通過調用accept()方法返回一個CONNECTOR.
CONNECTOR模式可以同時在客戶端和服務器端使用,它的代表類是SocketChannelUDT.
如果是在server端,則是通過調用server端的accept方法生成的。如果是在客戶端,則表示的是客戶端和服務器端之間的連接。
還有一種模式是RENDEZVOUS模式。這種模式表示的是連接的每一側都有對稱對等的channel。也就是一個雙向的模式,它的代表類是RendezvousChannelUDT。
構建ChannelFactory
上面提到的兩種Type和三種Kind都是用來定義channel的,所以如果將其混合,會生成六種不同的channelFactory,分別是:
public static final ChannelFactory<UdtServerChannel> BYTE_ACCEPTOR = new NioUdtProvider<UdtServerChannel>(TypeUDT.STREAM, KindUDT.ACCEPTOR);public static final ChannelFactory<UdtChannel> BYTE_CONNECTOR = new NioUdtProvider<UdtChannel>(TypeUDT.STREAM, KindUDT.CONNECTOR);public static final ChannelFactory<UdtChannel> BYTE_RENDEZVOUS = new NioUdtProvider<UdtChannel>(TypeUDT.STREAM, KindUDT.RENDEZVOUS);public static final ChannelFactory<UdtServerChannel> MESSAGE_ACCEPTOR = new NioUdtProvider<UdtServerChannel>(TypeUDT.DATAGRAM, KindUDT.ACCEPTOR);public static final ChannelFactory<UdtChannel> MESSAGE_CONNECTOR = new NioUdtProvider<UdtChannel>(TypeUDT.DATAGRAM, KindUDT.CONNECTOR);public static final ChannelFactory<UdtChannel> MESSAGE_RENDEZVOUS = new NioUdtProvider<UdtChannel>(TypeUDT.DATAGRAM, KindUDT.RENDEZVOUS);這些channelFactory通過調用newChannel()方法來生成新的channel。
但是歸根節(jié)點,這些channel最后是調用SelectorProviderUDT的from方法來生成channel的。
SelectorProviderUDT
SelectorProviderUDT根據TypeUDT的不同有兩種,分別是:
public static final SelectorProviderUDT DATAGRAM = new SelectorProviderUDT(TypeUDT.DATAGRAM);public static final SelectorProviderUDT STREAM = new SelectorProviderUDT(TypeUDT.STREAM);可以通過調用他的三個方法來生成對應的channel:
public RendezvousChannelUDT openRendezvousChannel() throws IOException {final SocketUDT socketUDT = new SocketUDT(type);return new RendezvousChannelUDT(this, socketUDT);}public ServerSocketChannelUDT openServerSocketChannel() throws IOException {final SocketUDT serverSocketUDT = new SocketUDT(type);return new ServerSocketChannelUDT(this, serverSocketUDT);}public SocketChannelUDT openSocketChannel() throws IOException {final SocketUDT socketUDT = new SocketUDT(type);return new SocketChannelUDT(this, socketUDT);}使用UDT
搭建好了netty服務器,剩下就是編寫Handler對數據進行處理了。
這里我們簡單的將客戶端寫入的數據再回寫。客戶端先創(chuàng)建一個message:
message = Unpooled.buffer(UDTEchoClient.SIZE);message.writeBytes("www.flydean.com".getBytes(StandardCharsets.UTF_8));再寫入到server端:
public void channelActive(final ChannelHandlerContext ctx) {log.info("channel active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions());ctx.writeAndFlush(message);}服務器端通過channelRead方法來接收:
public void channelRead(final ChannelHandlerContext ctx, Object msg) {ctx.write(msg);}總結
以上就是netty中使用UDT的原理和一個簡單的例子。
本文的例子可以參考:learn-netty4
本文已收錄于 http://www.flydean.com/40-netty-udt-support/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!
總結
以上是生活随笔為你收集整理的netty系列之:请netty再爱UDT一次的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 删除icloud照片手机照片会不见吗?看
- 下一篇: UDT 最新源码分析(三) -- UDT