UDP简介
什么是UDP协议
UDP(用户数据报协议)是一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向事务的简单不可靠信息传送服务。UDP协议基本上是IP协议与上层协议的接口,适用端口分别运行在同一台设备上的多个应用程序,同时UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等
UDP报文头
UDP报文头由4个域组成,每个域各占用2个字节,其中包括目的端口号和源端口号信息,数据报的长度域是指包括报头和数据部分在内的总字节数,校验值域来保证数据的安全。由于通讯不需要连接,所以可以实现广播发送
UDP与TCP的区别
TCP作为面向连接的传输协议,负责管理两个网络端点之间的连接的建立,在连接的生命周期内的有序和可靠的消息传输,以及最后,连接的有序终止。而相比之下,UDP无连接的协议,并没有持久化连接的概念,并且每个消息(一个 UDP数据报)都是一个单独的传输单元。此外,UDP也没有TCP的纠错机制,其中每个节点都将确认它们所接收到的包,而没有被确认的包将会被发送方重新传输
通过类比,TCP连接就像打电话,其中一系列的有序消息将会在两个方向上流动。相反, UDP则类似于往邮箱中投入一叠明信片。你无法知道它们将以何种顺序到达它们的目的地, 或者它们是否所有的都能够到达它们的目的地
并且UDP相对TCP其本身作为无连接的不可靠的传输协议,在传输过程中不会对数据包进行合并发送(TCP通常情况下存在Nagle算法会合并数据包),而是直接发送,因此既然不会对数据进行合并,每个数据包都是完整的(数据+UDP头+IP头等等发一次数据封装一次)也就没有粘包一说了
认知UDT
基于UDP的数据传输协议(UDP-based Data Transfer Protocol,简称 UDT)是一种互联网数据传输协议。UDT 的主要目的是支持高速广域网上的海量数据传输,最典型的例子就是建立在光纤广域网上的网格计算,一些研究所在这样的网络上运行他们的分布式的数据密集程序,例如,远程访问仪器、分布式数据挖掘和高分辨率的多媒体流
而互联网上的标准数据传输协议 TCP 在高带宽长距离网络上性能很差。顾名思义,UDT建于UDP 之上,并引入新的拥塞控制和数据可靠性控制机制。UDT 是面向连接的双向的应用层协议
UDT 的特性主要包括在以下几个方面:
- 面向连接的协议:面向连接意味着两个使用协议的应用在彼此交换数据之前必须先建立一个连接,当然 UDT 是逻辑上存在的连接通道。这种连接的维护是基于握手、Keep-alive(保 活)以及关闭连接
- 可靠的协议:依靠包序号机制、接收者的 ACK 响应和丢包报告、ACK 序号机制、重传机 制(基于丢包报告和超时处理)来实现数据传输的可靠性
- 双工的协议:每个 UDT 实例包含发送端和接收端的信息
- 新的拥塞算法,并且具有可扩展的拥塞控制框架:新的拥塞控制算法不同于基于窗口的 TCP 拥塞控制算法(慢启动和拥塞避免),是混合的基于窗口的、基于速率的拥塞控制算法。 可扩展的拥塞控制框架开源的代码和拥塞控制的 C++类架构,可支持开发者派生专用的拥塞 控制算法
- 带宽估计:UDT使用对包(PP – Packet pair)的机制来估计带宽值。即每 16 个包为一组, 最后一个是对包。即发送方不用等到下一个发送周期内再发送。接收方接收到对包后对其到达时间进行记录,可结合上次记录的值计算出链路的带宽(计算的方法称为中值过滤法), 并在下次 ACK 中进行反馈
Netty中UDP相关类
Netty中提供了大量的类来支持UDP应用程序的编写,主要包括:
名称 | 描述 |
---|---|
interface AddressedEnvelope<M, A extends SocketAddress>extends ReferenceCounted | 定义一个消息,其包装了另一个消息并带有发送者和接收者地址。其中 M 是消息类型; A 是地址类型 |
class DefaultAddressedEnvelope<M, A extends SocketAddress>implements AddressedEnvelope<M,A> | 提供了 interface AddressedEnvelope 的默认实现 |
class DatagramPacket extends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> implements ByteBufHolder | 扩展了 DefaultAddressedEnvelope 以使用 ByteBuf 作为消息数据容器,其中存在比较重要的方法:通过content()来获取消息内容、通过sender()来获取发送者的消息、通过recipient()来获取接收者的消息 |
interface DatagramChannel extends Channel | 扩展了 Netty 的 Channel 抽象以支持 UDP 的多播组管理 |
class NioDatagramChannnel extends AbstractNioMessageChannel implements DatagramChannel | 定义了一个能够发送和接收 Addressed-Envelope 消息的 Channel 类型 |
Netty 的 DatagramPacket 是一个简单的消息容器,DatagramChannel 实现用它来和远程节点通信。类似于在我们先前的类比中的明信片,它包含了接收者(和可选的发送者)的地址以及消息的有效负载本身
实现UDP单播
所谓的单播的传输模式,是定义为发送消息给一个由唯一的地址所标识的单一的网络目的地。而面向连接的协议和无连接协议都是支持这种模式的
首先,配置发送方,发送方将传输方式设置为NioDatagramChannel即通过UDP传输,然后将发送的UDP报文信息打包成DatagramPacket发送到指定的地址和端口
public class MyUdpQuestionSide {
private static final int PORT = 8761;
private static final String HOST = "127.0.0.1";
private static EventLoopGroup eventLoopGroup =new NioEventLoopGroup();
private static Bootstrap bootstrap = new Bootstrap();
public static void startQuestion(){
try {
bootstrap.group(eventLoopGroup).
channel(NioDatagramChannel.class).//指定UDP传输方式
handler(new MyQuestionHandler());
//由于UDP是无连接的,因此不需要建立连接
Channel channel= bootstrap.bind(0).sync().channel();
//将请求的UDP报文打包成DatagramPacket发送到接收方
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("question message!",CharsetUtil.UTF_8),new InetSocketAddress(HOST, PORT))).sync();
//由于不确定接收方是否能够收到报文,并且当前能否收到应答报文也不确定
//因此需要为channel设置等待10s,超时10s就关闭连接
if(channel.closeFuture().await(10000)){
System.out.println("请求结束");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {