netty 系列之: 使用 UDP 协议
简介在之前的系列文章中,我们到了使用 netty 做聊天服务器,聊天服务器使用的 SocketChannel,也就是说底层的协议使用的是 Scoket。今天我们将会给大家介绍如何在 netty 中使用 UDP 协议。
UDP 协议 UDP( User Datagram Protocol ),也叫用户数据报协议。
UDP 的主要功能和亮点并不在于它引入了什么特性,而在于它忽略的那些特性:不保证消息交付,不保证交付顺序,不跟踪连接状态,不需要拥塞控制。
我们来看一下 UDP 的数据包:
UDP 是一种无连接的协议,发送者只管发送数据包即可,并不负责处理和保证数据是否成功发送,数据是否被处理完成等。它的唯一作用就是发送。
在 JDK 中表示 UDP 的有一个专门的类叫做:java.net.DatagramPacket,在 NIO 中还有一个 java.nio.channels.DatagramChannel,专门负责处理 UDP 的 channel。
这里我们要将的是 netty,netty 中对于 UDP 协议也有上面的两个类,名字虽然是一样的,但是对应的包不同。他们分别是:
io.netty.channel.socket.DatagramPacket 和 io.netty.channel.socket.DatagramChannel,当然 netty 中的这两个类是对 JDK 自带类的增强。
先看一下 netty 中 DatagramPacket 的定义:
public class DatagramPacketextends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> implements ByteBufHolderDatagramPacket 类实现了 ByteBufHolder 接口,表示它里面存放的是 ByteBuf。然后他又继承自 DefaultAddressedEnvelope,这个类是对地址的封装,其中 ByteBuf 表示传递消息的类型,InetSocketAddress 表示目标的地址,它是一个 IP 地址+端口号的封装。
从上面的 UDP 协议我们知道,UDP 只需要知道目标地址和对应的消息即可,所以 DatagramPacket 中包含的数据已经够用了。
DatagramChannel 是用来传递 DatagramPacket 的,因为 DatagramChannel 是一个接口,所以一般使用 NioDatagramChannel 作为真正使用的类。
String 和 ByteBuf 的转换之前我们讲到过,netty 中的 channel 只接受 ByteBuf 数据类型,如果直接写入 String 会报错,之前的系列文章中,我们讲过两种处理方法,第一种是使用 ObjectEncoder 和 ObjectDecoder 在写入 ByteBuf 之前,对对象进行序列化,这一种不仅适合 String,也适合 Object 对象。
第二种是使用 StringEncoder 和 StringDecoder 专门处理 String 的 encode 和 decode,这种处理只能处理 String 的转换,对 Object 无效。
如果你不想使用这些 encoder 和 decoder 还可以直接使用 ByteBuf 和 String 进行转换。
比如要将 String 写入 ByteBuf 可以调用 Unpooled.copiedBuffer 的命令如下:
Unpooled.copiedBuffer("开始广播", CharsetUtil.UTF_8)将 ByteBuf 转换成为 String 则可以调用:
byteBuf.toString(CharsetUtil.UTF_8)构建 DatagramPacketDatagramPacket 总共可以接受三个参数,分别是要发送的数据 data,要接收数据包的地址和要发送数据包的地址。
这里我们并不关心发送数据包的地址,那么只需要两个参数即可,对于客户端来说,我们发送一个”开始广播“的消息给服务器端,告诉服务器端可以向客户发送回复消息了,如下所示:
new DatagramPacket(Unpooled.copiedBuffer("开始广播", CharsetUtil.UTF_8),SocketUtils.socketAddress("255.255.255.255", PORT))上我们使用 SocketUtils.socketAddress 创建了一个特殊的地址,255.255.255.255 是一个特殊的广播地址,意味着所有的主机,因为我们的客户端并不知道服务器的地址,所以使用 255.255.255.255 来广播。
构建好的 DatagramPacket,里面有一个 sender()方法,可以用来获取客户端的地址,所以在服务器端可以这样构建要发送的 packge:
new DatagramPacket(Unpooled.copiedBuffer("广播: " + nextQuote(), CharsetUtil.UTF_8), packet.sender())启动客户端和服务器 UDP 的客户端和服务器启动和 socket 稍微有所不同,如果是 socket,那么使用的 channel 是 NioSocketChannel,如果是 UDP,则使用的是 NioDatagramChannel。如下是服务器端启动的代码:
EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).handler(new UDPServerHandler());
注意,这里我们需要设置 ChannelOption.SO_BROADCAST 为 true,因为 UDP 是以广播的形式发送消息的。
客户端的实现和 socket 稍微有所不同,下面是客户端的启动实现:
EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).handler(new UDPClientHandler());
对于 UDP 来说,并不存在地址绑定一说,所以上 Bootstrap 调用 bind(0)。
总结本文讲解了 netty 中 UDP 协议的实现,UDP 相较于 Socket 连接而言更加简单。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/11-netty-udp/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/43e930fe37810a83201e32db2】。文章转载请联系作者。
评论