【十 一】Netty UDP协议栈开发

介绍

UDP 是用户数据报协议(User Datagram Protocol) 的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事物的简单信息传送服务。与TCP协议不同,UDP协议直接利用IP协议进行UDP数据报的传输,UDP提供的是面向无连接的,不可靠的数据报投递服务。当使用UDP协议传输信息时,用户程序必须负责解决数据报丢失,重复,排序,差错确认等问题。
由于UDP具有资源消耗小处理速度快的优点,故通常视频,音频等可靠性要求不高的数据传输一般会使用UDP,即使有一定的丢包率,也不会对功能造成严重的影响。

协议简介

UDP是无连接的,通信双方不需要建立物理链路连接。在网络中他用于处理数据包,在IOS模型中,它处于第四层传输,位于IP协议的上一层。他不对数据报分组,组装,校验,排序。因此是不可靠的。报文的发送者不知道报文是否被对方正确接收。
UDP数据报格式有首部和数据两个部分,首部很简单,为8个字节,包括以下几点:
(1)源端口,2个字节,最大值 为 65535
(2)目的端口,两个字节,最大值 65535
(3)长度,2个字节,UDP用户数据报的总长度
(4)校验和,2字节,用于校验UDP数据报的数字段和包含UDP数据报首部的"伪首部"。其校验方法类似于IP分组首部中的首部校验和。

伪首部

又称为伪包头(Pseudo Header):是指在TCP的分段或UDP的数据报格式中,在数据报首部前面增加源IP地址,目的IP地址,IP分组的协议字段,TCP或UDP数据报的总长度,共12字节,所构成的扩展首部结构。此伪首部是一个临时的结构,它既不向上也不向下传递,仅仅是为了保证可以校验套接字的正确性。

UDP协议数据报格式如图:
在这里插入图片描述

UDP协议的特点

(1)UDP传送数据前并不与对方建立连接,即无连接。在传输数据前,发送方和接收方互相交换信息使双方同步
(2)UDP对接收到的数据报不发送确认信号,发送端不知道数据是否被正确接收,也不会重发数据。
(3)UDP传送数据比TCP快速,系统开销也少。UDP比较简单,UDP头包含了源端口,目的端口,消息长度和校验和等很少的字节。由于UDP比TCP简单,灵活,常用于可靠性要求不高的数据传输,如视频,图片以及简单文件传输系统(TFTP)。TCP则适合可靠性要求很高但实时性要求不高的应用,如文件传输协议FTP,超文本传输协议HTTP,简单邮件传输协议SMTP等。

开发

jar依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId> <!-- Use 'netty5-all' for 5.0-->
            <version>5.0.0.Alpha1</version>
            <scope>compile</scope>
        </dependency>

UDP 服务端启动类

public class ITTechnologyServer {
    public void run(int port){
        //UDP 通信不需要建立链路,所以代码相对TCP 更加简单一些。
        EventLoopGroup group=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            //因为不存在连接,故不用设置ChannelPipeline
            bootstrap.group(group)
                    //UDP 采用 NioDatagramChannel
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST,true)
                    //业务处理类
                    .handler(new ITTechnologyServerHandler());
            bootstrap.bind(port).sync().channel().closeFuture().await();
            System.out.println("unp server is started....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ITTechnologyServer().run(8080);
    }
}

服务端业务处理类

public class ITTechnologyServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    //技术列表
    private static final String[] IT_DICTIONARY={"Java","Redis","MySql","Netty","TCP","Oracle",
            "RabbitMQ","ElasticSearch","SpringBoot","Dubbo"};
    public static final String IT_Technology_LEARN="学习哪项 IT技术?";
    public static final String IT_Technology_RESULT="IT 技术学习成果";


    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
        String req=datagramPacket.content().toString(CharsetUtil.UTF_8);
        System.out.println(req);
        if (IT_Technology_LEARN.equals(req)){
            channelHandlerContext.writeAndFlush(
                    new DatagramPacket(Unpooled.copiedBuffer(IT_Technology_RESULT+":"+nextITTechnology(),
                            CharsetUtil.UTF_8),datagramPacket.sender()));
        }
    }

    private String nextITTechnology(){
        //采用安全随机类,随机获取一个技术
        int ITTechnologyId= ThreadLocalRandom.current().nextInt(IT_DICTIONARY.length);
        return IT_DICTIONARY[ITTechnologyId];
    }

    public void exceptionCaught(ChannelHandlerContext context,Throwable cause){
        context.close();
        cause.printStackTrace();
    }
}

客户端启动类

public class ITTechnologyClient {
    private static final String IP="255.255.255.255";
    public void run(int port){
        EventLoopGroup group=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST,true)
                    .handler(new ITTechnologyClientHandler());
            //该应用占用8888端口,不能和server 的端口相同
            Channel channel=bootstrap.bind(8888).sync().channel();
            //向网段内的所有机器广播UDP消息
            channel.writeAndFlush(new DatagramPacket(
                    Unpooled.copiedBuffer(ITTechnologyServerHandler.IT_Technology_LEARN, CharsetUtil.UTF_8),
                    new InetSocketAddress(IP,port))).sync();
            if (!channel.closeFuture().await(15000)){
                System.out.println("查询超时!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {

        new ITTechnologyClient().run(8080);
    }
}

客户端业务处理类

public class ITTechnologyClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    public void exceptionCaught(ChannelHandlerContext context,Throwable cause){
        cause.printStackTrace();
        context.close();
    }

    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramChannel) throws Exception {
        String response=datagramChannel.content().toString(CharsetUtil.UTF_8);
        if (response.startsWith(ITTechnologyServerHandler.IT_Technology_RESULT)){
            System.out.println(response);
            channelHandlerContext.close();
        }
    }
}

代码说明

代码逻辑比较简单。相对于TCP协议,启动类也简单多了。需要注意的是,启动类 server 和client 需要占用不同的端口。并且没有了连接的步骤。也不用添加解码器,Netty 已经支持。

测试

服务端打印截图:

在这里插入图片描述

客户端打印截图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试结果

每次结果都不一样,并且打印结果正常。说明在这个过程中没有发送丢包和乱序的问题。Netty 对其做了一些优化控制。

总结

本章详细介绍了利用Netty进行UDP服务端和客户端开发。代码比较简单。
然后对UDP协议进行了介绍。大家可以动手试试。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值