热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

UDP协议开发

UDP是用户数据报协议(UserDatagramProtocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务。与TCP协议不同,UD

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

UDP协议简介

UDP是无连接的,通信双方不需要建立物理链路连接。在网络中它用于处理数据包,在OSI模型中,它处于第四层传输层,即位于IP协议的上一层。它不对数据报分组、组装、校验和排序,因此是不可靠的。报文的发送者不知道报文是否被对方正确接收。

UDP数据报格式有首部和数据两个部分,首部很简单,为8个字节,包括以下部分:

(1)源端口:源端口号,2个字节,最大值为65535;

(2)目的端口:目的端口号,2个字节,最大值为65535;

(3)长度:2字节,UDP用户数据报的总长度;

(4)校验和:2字节,用于校验UDP数据报的数字段和包含UDP数据报首部的“伪首部”。其校验方法类似于IP分组首部中的首部校验和。

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

UDP协议数据报格式示意图如图:

技术分享

UDP协议的特点如下。

(1)UDP传送数据前并不与对方建立连接,即UDP是无连接的。在传输数据前,发送方和接收方相互交换信息使双方同步;

(2)UDP对接收到的数据报不发送确认信号,发送端不知道数据是否被正确接收,也不会重发数据;

(3)UDP传送数据比TCP快速,系统开销也少:UDP比较简单,UDP头包含了源端口、目的端口、消息长度和校验和等很少的字节。由于UDP比TCP简单、灵活,常用于可靠性要求不高的数据传输,如视频、图片以及简单文件传输系统(TFTP)等。TCP则适用于可靠性要求很高但实时性要求不高的应用,如文件传输协议FTP、超文本传输协议HTTP、简单邮件传输协议SMTP等。

服务端开发

 

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 group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            //由于使用UDP通信,在创建Channel的时候需要通过NioDatagramChannel来创建
            b.group(group).channel(NioDatagramChannel.class)
                    //随后设置Socket参数支持广播,
                    .option(ChannelOption.SO_BROADCAST, true)
                    //最后设置业务处理handler。
                    //相比于TCP通信,UDP不存在客户端和服务端的实际连接,
                    //因此不需要为连接(ChannelPipeline)设置handler,
                    //对于服务端,只需要设置启动辅助类的handler即可。
                    .handler(new ChineseProverbServerHandler());
            b.bind(port).sync().channel().closeFuture().await();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        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 {
    // 谚语列表
    private static final String[] DICTIOnARY= {"只要功夫深,铁棒磨成针。",
            "旧时王谢堂前燕,飞入寻常百姓家。", "洛阳亲友如相问,一片冰心在玉壶。", "一寸光阴一寸金,寸金难买寸光阴。",
            "老骥伏枥,志在千里。烈士暮年,壮心不已!"};

    private String nextQuote() {
        //由于ChineseProverbServerHandler存在多线程并发操作的可能,
        //所以使用了Netty的线程安全随机类ThreadLocalRandom。
        // 如果使用的是JDK7,可以直接使用JDK7的java.util.concurrent.ThreadLocalRandom。
        int quoteId = ThreadLocalRandom.current().nextInt(DICTIONARY.length);
        return DICTIONARY[quoteId];
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg)throws Exception {
        //Netty对UDP进行了封装,因此,接收到的是Netty封装后的io.netty. channel.socket.DatagramPacket对象。
        DatagramPacket packet = (DatagramPacket) msg;
        //将packet内容转换为字符串(利用ByteBuf的toString(Charset)方法),
        String req = packet.content().toString(CharsetUtil.UTF_8);
        System.out.println(req);
        // 然后对请求消息进行合法性判断:如果是“谚语字典查询?”,则构造应答消息返回。
        // DatagramPacket有两个参数:第一个是需要发送的内容,为ByteBuf;
        // 另一个是目的地址,包括IP和端口,可以直接从发送的报文DatagramPacket中获取。
        if ("谚语字典查询?".equals(req)) {
            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();
    }
}

客户端开发 

UDP程序的客户端和服务端代码非常相似,唯一不同之处是UDP客户端会主动构造请求消息,向本网段内的所有主机广播请求消息,对于服务端而言,接收到广播请求消息之后会向广播消息的发起方进行定点发送。

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;

import java.net.InetSocketAddress;

public class ChineseProverbClient {

    public void run(int port) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            //创建UDP Channel和设置支持广播属性等与服务端完全一致。
            // 由于不需要和服务端建立链路,UDP Channel创建完成之后,客户端就要主动发送广播消息;
            // TCP客户端是在客户端和服务端链路建立成功之后由客户端的业务handler发送消息,这就是两者最大的区别。
            b.group(group).channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new ChineseProverbClientHandler());
            Channel ch = b.bind(0).sync().channel();
            // 向网段内的所有机器广播UDP消息
            // 用于构造DatagramPacket发送广播消息,
            // 注意,广播消息的IP设置为“255.255.255.255”。
            // 消息广播之后,客户端等待15s用于接收服务端的应答消息,然后退出并释放资源。
            ch.writeAndFlush(
                    new DatagramPacket(Unpooled.copiedBuffer("谚语字典查询?",CharsetUtil.UTF_8),
                            new InetSocketAddress("255.255.255.255", port))
            ).sync();
            if (!ch.closeFuture().await(15000)) {
                System.out.println("查询超时!");
            }
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        new ChineseProverbClient().run(port);
    }
}
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 {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object o)
            throws Exception {
        //接收到服务端的消息之后将其转成字符串,然后判断是否以“谚语查询结果:”开头,
        //如果没有发生丢包等问题,数据是完整的,就打印查询结果,然后释放资源。
        DatagramPacket msg = (DatagramPacket)o;
        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();
    }
}

UDP协议开发


推荐阅读
  • 本报告记录了嵌入式软件设计课程中的第二次实验,主要探讨了使用KEIL V5开发环境和ST固件库进行GPIO控制及按键响应编程的方法。通过实际操作,加深了对嵌入式系统硬件接口编程的理解。 ... [详细]
  • 本文探讨了线性表中元素的删除方法,包括顺序表和链表的不同实现策略,以及这些策略在实际应用中的性能分析。 ... [详细]
  • 实现Win10与Linux服务器的SSH无密码登录
    本文介绍了如何在Windows 10环境下使用Git工具,通过配置SSH密钥对,实现与Linux服务器的无密码登录。主要步骤包括生成本地公钥、上传至服务器以及配置服务器端的信任关系。 ... [详细]
  • 笔记说明重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系& ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 探索Java 11中的ZGC垃圾收集器
    Java 11引入了一种新的垃圾收集器——ZGC,由Oracle公司研发,旨在支持TB级别的内存容量,并保证极低的暂停时间。本文将探讨ZGC的开发背景、技术特点及其潜在的应用前景。 ... [详细]
  • 本文探讨了使用普通生成函数和指数生成函数解决组合与排列问题的方法,特别是在处理特定路径计数问题时的应用。文章通过详细分析和代码实现,展示了如何高效地计算在给定条件下不相邻相同元素的排列数量。 ... [详细]
  • 在Notepad++中配置Markdown语法高亮及实时预览功能
    本文详细介绍了如何在Notepad++中配置Markdown语法高亮和实时预览功能,包括必要的插件安装和设置步骤。 ... [详细]
  • 探讨如何在映射文件中处理重复的属性字段,以避免数据操作时出现错误。 ... [详细]
  • 网络流24题——试题库问题
    题目描述:假设一个试题库中有n道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取m道题组成试卷。并要求试卷包含指定类型的试题。试设计一个满足要求的组卷算 ... [详细]
  • 利用无代码平台实现高效业务应用开发
    随着市场环境的变化加速,全球企业都在探索更为敏捷的应用开发模式,以便快速响应新兴的商业机遇。然而,传统的软件开发方式不仅成本高昂,而且耗时较长,这往往导致IT与业务部门之间的合作障碍,进而影响项目的成功。本文将探讨如何通过无代码开发平台解决这些问题。 ... [详细]
  • 为何Compose与Swarm之后仍有Kubernetes的诞生?
    探讨在已有Compose和Swarm的情况下,Kubernetes是如何以其独特的设计理念和技术优势脱颖而出,成为容器编排领域的领航者。 ... [详细]
  • 本文介绍了如何通过安装 sqlacodegen 和 pymysql 来根据现有的 MySQL 数据库自动生成 ORM 的模型文件(model.py)。此方法适用于需要快速搭建项目模型层的情况。 ... [详细]
  • 本文探讨了程序员这一职业的本质,认为他们是专注于问题解决的专业人士。文章深入分析了他们的日常工作状态、个人品质以及面对挑战时的态度,强调了编程不仅是一项技术活动,更是个人成长和精神修炼的过程。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
author-avatar
ka-ka快乐_848
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有