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