UDP是一种无连接协议,相对于TCP协议而言,简单高效,适用于传输视频、音频等及时性要求高,但是准确率要求低的数据。Netty对UDP传输数据也进行了封装,实现起来特别简单。
首先是编写服务端启动类:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
public class ChineseProverbServer
{
public void run(int port)throws Exception{
EventLoopGroup bossGroup=new NioEventLoopGroup();
try
{
//通过NioDatagramChannel创建Channel,并设置Socket参数支持广播
//UDP相对于TCP不需要在客户端和服务端建立实际的连接,因此不需要为连接(ChannelPipeline)设置handler
Bootstrap b=new Bootstrap();
b.group(bossGroup)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChineseProverbServerHandler());
b.bind(port).sync().channel().closeFuture().await();
}
catch (Exception e)
{
e.printStackTrace();
}
finally{
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args)throws Exception
{
int port=8888;
if (args!=null&&args.length>0)
{
port=Integer.valueOf(args[0]);
}
new ChineseProverbServer().run(port);
}
}
然后是服务端辅助处理类:
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.ThreadLocalRandom;
public class ChineseProverbServerHandler extends
SimpleChannelInboundHandler<DatagramPacket>
{
private static final String [] DICTIONARY={"小葱拌豆腐,一穷二白","只要功夫深,铁棒磨成针","山中无老虎,猴子称霸王"};
private String nextQuote(){
//线程安全岁基类,避免多线程环境发生错误
int quote=ThreadLocalRandom.current().nextInt(DICTIONARY.length);
return DICTIONARY[quote];
}
//接收Netty封装的DatagramPacket对象,然后构造响应消息
@Override
public void messageReceived(ChannelHandlerContext ctx,DatagramPacket packet)
throws Exception{
//利用ByteBuf的toString()方法获取请求消息
String req=packet.content().toString(CharsetUtil.UTF_8);
System.out.println(req);
if("谚语字典查询?".equals(req)){
//创建新的DatagramPacket对象,传入返回消息和目的地址(IP和端口)
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(
"谚语查询结果:"+nextQuote(),CharsetUtil.UTF_8), packet.sender()));
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause)
throws Exception{
ctx.close();
cause.printStackTrace();
}
}
下面是客户端启动类:
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
public class ChineseProverbClient
{
public void run(int port)throws Exception{
EventLoopGroup group=new NioEventLoopGroup();
try
{
Bootstrap b=new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChineseProverbClientHandler());
Channel ch=b.bind(0).sync().channel();
//向网段内的所有机器广播
ch.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(
"谚语字典查询?",CharsetUtil.UTF_8), new InetSocketAddress(
"255.255.255.255",port))).sync();
//客户端等待15s用于接收服务端的应答消息,然后退出并释放资源
if(!ch.closeFuture().await(15000)){
System.out.println("查询超时!");
}
}
catch (Exception e)
{
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
public static void main(String[] args)throws Exception
{
int port=8888;
if (args!=null&&args.length>0)
{
port=Integer.valueOf(args[0]);
}
new ChineseProverbClient().run(port);
}
}
客户端的作用就是通过广播向局域网内所有的机器广播消息,如果存在启动的监听者就会收到消息,然后根据广播者的身份(IP和端口),发送响应消息。
下面就是客户端启动类的辅助处理类:
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
public class ChineseProverbClientHandler extends
SimpleChannelInboundHandler<DatagramPacket>
{
@Override
public void messageReceived(ChannelHandlerContext ctx,DatagramPacket
msg)throws Exception{
String response=msg.content().toString(CharsetUtil.UTF_8);
if(response.startsWith("谚语查询结果:")){
System.out.println(response);
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,Throwable
cause)throws Exception{
cause.printStackTrace();
ctx.close();
}
启动服务端后,多次启动客户端就可以在控制台看到服务端的响应。
参考书籍《Netty权威指南》