簡介
今天我將會(huì)給大家介紹如何在netty中使用UDP協(xié)議。
UDP協(xié)議
UDP( User Datagram Protocol ),也叫用戶數(shù)據(jù)報(bào)協(xié)議。
UDP 的主要功能和亮點(diǎn)并不在于它引入了什么特性,而在于它忽略的那些特性:不保證消息交付,不保證交付順序,不跟蹤連接狀態(tài),不需要擁塞控制。
我們來看一下UDP的數(shù)據(jù)包:
UDP是一種無連接的協(xié)議,發(fā)送者只管發(fā)送數(shù)據(jù)包即可,并不負(fù)責(zé)處理和保證數(shù)據(jù)是否成功發(fā)送,數(shù)據(jù)是否被處理完成等。它的唯一作用就是發(fā)送。
在JDK中表示UDP的有一個(gè)專門的類叫做:java.net.DatagramPacket,在NIO中還有一個(gè)java.nio.channels.DatagramChannel,專門負(fù)責(zé)處理UDP的channel。
這里我們要將的是netty,netty中對于UDP協(xié)議也有上面的兩個(gè)類,名字雖然是一樣的,但是對應(yīng)的包不同。他們分別是:
io.netty.channel.socket.DatagramPacket 和 io.netty.channel.socket.DatagramChannel,當(dāng)然netty中的這兩個(gè)類是對JDK自帶類的增強(qiáng)。
先看一下netty中DatagramPacket的定義:
public class DatagramPacket
extends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> implements ByteBufHolder
DatagramPacket類實(shí)現(xiàn)了ByteBufHolder接口,表示它里面存放的是ByteBuf。然后他又繼承自DefaultAddressedEnvelope,這個(gè)類是對地址的封裝,其中ByteBuf表示傳遞消息的類型,InetSocketAddress表示目標(biāo)的地址,它是一個(gè)IP地址+端口號的封裝。
從上面的UDP協(xié)議我們知道,UDP只需要知道目標(biāo)地址和對應(yīng)的消息即可,所以DatagramPacket中包含的數(shù)據(jù)已經(jīng)夠用了。
DatagramChannel是用來傳遞DatagramPacket的,因?yàn)镈atagramChannel是一個(gè)接口,所以一般使用NioDatagramChannel作為真正使用的類。
String和ByteBuf的轉(zhuǎn)換
之前我們講到過,netty中的channel只接受ByteBuf數(shù)據(jù)類型,如果直接寫入String會(huì)報(bào)錯(cuò),之前的系列文章中,我們講過兩種處理方法,第一種是使用ObjectEncoder和ObjectDecoder在寫入ByteBuf之前,對對象進(jìn)行序列化,這一種不僅適合String,也適合Object對象。
第二種是使用StringEncoder和StringDecoder專門處理String的encode和decode,這種處理只能處理String的轉(zhuǎn)換,對Object無效。
如果你不想使用這些encoder和decoder還可以直接使用ByteBuf和String進(jìn)行轉(zhuǎn)換。
比如要將String寫入ByteBuf可以調(diào)用Unpooled.copiedBuffer的命令如下:
Unpooled.copiedBuffer("開始廣播", CharsetUtil.UTF_8)
將ByteBuf轉(zhuǎn)換成為String則可以調(diào)用:
byteBuf.toString(CharsetUtil.UTF_8)
構(gòu)建DatagramPacket
DatagramPacket總共可以接受三個(gè)參數(shù),分別是要發(fā)送的數(shù)據(jù)data,要接收數(shù)據(jù)包的地址和要發(fā)送數(shù)據(jù)包的地址。
這里我們并不關(guān)心發(fā)送數(shù)據(jù)包的地址,那么只需要兩個(gè)參數(shù)即可,對于客戶端來說,我們發(fā)送一個(gè)”開始廣播“的消息給服務(wù)器端,告訴服務(wù)器端可以向客戶發(fā)送回復(fù)消息了,如下所示:
new DatagramPacket(
Unpooled.copiedBuffer("開始廣播", CharsetUtil.UTF_8),
SocketUtils.socketAddress("255.255.255.255", PORT))
上我們使用SocketUtils.socketAddress創(chuàng)建了一個(gè)特殊的地址,255.255.255.255是一個(gè)特殊的廣播地址,意味著所有的主機(jī),因?yàn)槲覀兊目蛻舳瞬⒉恢婪?wù)器的地址,所以使用255.255.255.255來廣播。
構(gòu)建好的DatagramPacket,里面有一個(gè)sender()方法,可以用來獲取客戶端的地址,所以在服務(wù)器端可以這樣構(gòu)建要發(fā)送的packge:
new DatagramPacket(
Unpooled.copiedBuffer("廣播: " + nextQuote(), CharsetUtil.UTF_8), packet.sender())
啟動(dòng)客戶端和服務(wù)器
UDP的客戶端和服務(wù)器啟動(dòng)和socket稍微有所不同,如果是socket,那么使用的channel是NioSocketChannel,如果是UDP,則使用的是NioDatagramChannel。如下是服務(wù)器端啟動(dòng)的代碼:
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new UDPServerHandler());
b.bind(PORT).sync().channel().closeFuture().await();
} finally {
group.shutdownGracefully();
}
注意,這里我們需要設(shè)置ChannelOption.SO_BROADCAST為true,因?yàn)閁DP是以廣播的形式發(fā)送消息的。
客戶端的實(shí)現(xiàn)和socket稍微有所不同,下面是客戶端的啟動(dòng)實(shí)現(xiàn):
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new UDPClientHandler());
Channel ch = b.bind(0).sync().channel();
對于UDP來說,并不存在地址綁定一說,所以上Bootstrap調(diào)用bind(0)。文章來源:http://www.zghlxwxcb.cn/news/detail-427036.html
總結(jié)
本文講解了netty中UDP協(xié)議的實(shí)現(xiàn),UDP相較于Socket連接而言更加簡單。文章來源地址http://www.zghlxwxcb.cn/news/detail-427036.html
到了這里,關(guān)于Netty網(wǎng)絡(luò)編程(五):使用UDP協(xié)議的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!