使用Netty进行UDP协议开发

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权威指南》

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值