文章目录
- UDP协议
- 1、什么是UDP协议?
- 一、定义与基本概念
- 二、主要特点
- 三、报文格式
- 四、应用场景
- 五、总结
- 2、如何使用Java中的UDP套接字?
- 一、UDP常用API
- DatagramSocket
- DatagramPacket
- 二、UDP协议下的客户端-服务器
- 服务器
- 客户端
UDP协议
UDP协议,全称为用户数据报协议(User Datagram Protocol),是一种工作在OSI(开放系统互连)模型中传输层的无连接协议。以下是关于UDP协议的详细介绍:
1、什么是UDP协议?
一、定义与基本概念
- 定义:UDP为应用程序提供了一种以最少的协议机制向其他程序发送消息的过程。它使用IP作为底层协议,为应用层提供一种简单的、不可靠的、无连接的数据传输服务。
- 标准:UDP的正式规范由IETF(互联网工程任务组)提供,具体为RFC 768。
二、主要特点
- 无连接:UDP在发送数据前不进行连接,发送结束时也没有连接可以释放,这减少了开销和发送数据之前的时延。
- 不可靠:UDP不保证数据包的可靠传输,如果数据包在传输过程中丢失或损坏,UDP不会尝试重新发送或恢复数据。使用UDP的应用需要自己处理数据包的丢失和错误检测。
- 面向数据报:UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。UDP一次交付完整的报文,因此应用程序必须选择合适大小的报文。
- 无拥塞控制:UDP没有拥塞控制机制,这使其适合那些允许网络拥塞时丢失数据,但不允许数据有太大时延的实时应用。
- 支持多播和广播:UDP支持一对一、一对多、多对一和多对多的交互通信,适用于需要在多个接收方之间共享数据的应用。
- 首部开销小:UDP只有8个字节的首部,相比于TCP的20个字节首部,UDP的额外开销更小,使得UDP在传输小数据包时更加高效。
三、报文格式
UDP报文由首部和数据两部分组成。首部包含以下字段(均用16位二进制数表示(2字节)):
- 源端口:源端口号,在要求对方回信时选用,不要求时可使用全0。
- 目的端口:目的端口号,在终点交付报文时必须使用。
- 长度:UDP用户数据报的长度,包括首部和数据,其最小值是8(仅有首部),最大值为65535,单位为字节,因此一个UDP数据报最多能承载的数据为64KB。
- 检验和:采用CRC校验法用于检测UDP用户数据包在传输中是否有错,如果出错则将报文丢弃。
四、应用场景
由于UDP具有传输速度快、延迟低、开销小等特点,它被广泛应用于对实时性要求较高的场景,如:
- 视频和音频流传输:如视频会议、实时直播等,UDP能够提供快速且高质量的数据传输。
- 在线游戏:实时多人在线游戏对数据传输的实时性要求极高,UDP的快速处理海量数据包的能力使其成为这类应用的理想选择。
- DNS查询:DNS系统使用UDP协议进行域名解析,能够快速检查DNS错误并确保网址的迅速解析。
- 网络广播:如校园广播、公司内部通知广播等,UDP的广播功能能够迅速、高效地传达信息给所有接收者。
五、总结
UDP协议以其无连接、不可靠、基于数据报的传输方式,在网络通信中发挥着重要作用。虽然它无法保证数据的可靠传输,但其快速、低延迟和高效的特点,使得UDP成为许多实时性要求高的应用的理想选择。
2、如何使用Java中的UDP套接字?
一、UDP常用API
DatagramSocket
-
基本概念
DatagramSocket
类位于Java的java.net包中,它提供了发送和接收数据报(datagram)的功能,这些数据报是通过UDP协议进行传输的。- DatagramSocket本身不维护状态,不能产生IO流,其唯一作用就是接收和发送数据报。数据报的发送和接收通常是通过与DatagramSocket配合使用的
DatagramPacket
对象来完成的。
-
构造方法
DatagramSocket提供了多种构造函数,以便创建绑定到特定端口或IP地址的套接字。常见的构造方法包括:
DatagramSocket()
:创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口,常用于客户端系统。DatagramSocket(int port)
:创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口,常用于服务器系统。DatagramSocket(int port, InetAddress laddr)
:创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。
-
主要功能
- 发送数据报:通过调用DatagramSocket的
send(DatagramPacket p)
方法,可以将封装了数据、目标地址和端口的DatagramPacket对象发送出去。DatagramSocket本身并不知道数据报的具体目的地,这些信息由DatagramPacket对象提供。 - 接收数据报:通过调用DatagramSocket的
receive(DatagramPacket p)
方法,可以接收一个数据报。这个方法会阻塞调用它的线程,直到接收到一个数据报为止。接收到的数据报会存储在传入的DatagramPacket对象的字节数组中。
- 发送数据报:通过调用DatagramSocket的
-
高级特性
- 设置超时:可以通过调用
setSoTimeout(int timeout)
方法为DatagramSocket设置接收操作的超时时间。如果在指定的时间内没有接收到数据报,则接收操作将抛出SocketTimeoutException异常。 - 广播和多播:UDP协议支持广播和多播。广播允许将数据报发送到网络上的所有设备,而多播则允许将数据发送到特定组的多个主机。在Java中,可以通过设置DatagramPacket的目标地址为广播地址(如255.255.255.255)来实现广播发送。多播则需要使用专门的MulticastSocket类。
- 设置超时:可以通过调用
-
注意事项
- 数据完整性:由于UDP协议不保证消息传递的可靠性,因此在使用DatagramSocket进行通信时,需要在应用程序层面实现传输可靠性,如通过重传机制等。
- 资源管理:使用完DatagramSocket后,应该调用其
close()
方法释放资源,以避免资源泄露。
DatagramPacket
-
基本概念
- DatagramPacket是Java中用于网络通信的一种数据包,特别是在UDP协议的通信中扮演着重要角色。
- DatagramPacket类封装了数据报的内容和目标地址信息,以便在网络上进行传输。数据报(Datagram)是网络层中的传输单元,它可能被压缩成一个或几个数据包(Packet)在数据链路层中传输。在Java中,DatagramPacket就是用来实现这种数据报传输的类。
-
主要功能
- 数据报发送:通过创建一个
DatagramPacket
对象,可以将数据报的内容和目标地址信息封装起来。然后,可以使用DatagramSocket
类的send()
方法将该数据报发送到网络上的目标地址。 - 数据报接收:通过创建一个
DatagramPacket
对象,可以指定一个缓冲区来接收从网络上接收到的数据报。然后,可以使用DatagramSocket
类的receive()
方法将接收到的数据报存储到该缓冲区中。 - 数据报解析:通过使用
DatagramPacket
对象的方法,可以获取数据报的内容、长度、目标地址等信息。这些信息可以用于解析和处理接收到的数据报。
- 数据报发送:通过创建一个
-
构造方法
DatagramPacket类提供了多个构造方法,以适应不同的使用场景:
DatagramPacket(byte[] buf, int length)
:用于接收数据,buf是保存传入数据报的缓冲区,length是要读取的字节数。DatagramPacket(byte[] buf, int offset, int length)
:用于接收数据,在缓冲区中指定了偏移量。DatagramPacket(byte[] buf, int length, InetAddress address, int port)
:用于发送数据,buf是包数据,length是包长度,address是目的地址,port是目的端口号。DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
:用于发送数据,同时指定了数据在缓冲区中的偏移量。
-
关键方法
getData()
:返回数据缓冲区。getLength()
:返回将要发送或接收到的数据的长度。getPort()
:返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。getAddress()
:返回某台机器的IP地址,此数据报将要发往该机器或者是从该机器接收到的。setData(byte[] buf, int offset, int length)
:为此包设置数据缓冲区。setAddress(InetAddress iaddr)
:设置要将此数据报发往的那台机器的IP地址。setPort(int port)
:设置要将此数据报发往的远程主机上的端口号。
-
注意事项
- 使用DatagramSocket和DatagramPacket进行网络通信时,需要先建立DatagramSocket对象,然后使用DatagramPacket对象作为传输数据的载体。
- 由于UDP协议是无连接的协议,因此通信双方都需要先建立一个DatagramSocket对象,才能进行通信。
- UDP协议不保证数据包的顺序、不保证数据包的可靠性,也不保证数据包一定能够到达对方,因此在设计应用时需要考虑到这些因素。
二、UDP协议下的客户端-服务器
以下是关于如何建立一个Udp为协议的回显客户端-服务器程序:
服务器
- 接收请求
创建一个DatagramSocket
用于监听某个服务器的固定端口(服务器需要绑定特定端口这样客户端才能发送请求到服务器);
创建空数据报DatagramPacket
,接收请求。 - 解析请求
将数据报的内容解析成字符串。 - 根据请求计算响应
对已解析的请求进行计算得到响应内容。 - 将响应传回客户端
将响应内容打包成数据报传回客户端。
public class UdpEchoServer {
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
// 创建一个socket对象监听某个固定端口
socket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true) {
// 1.读取请求并解析
// 用一个空的数据报接收请求
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(requestPacket);
// 2.解析请求,将数据报的内容解析成字符串
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
// 3.根据请求计算响应
String response = process(request);
// 4.把响应写回到客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0,response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d] rep:%s, resp: %s\n",requestPacket.getAddress(),requestPacket.getPort(),
request,response);
}
}
public String process(String request){
return request;
}
}
客户端
- 生成请求并发送
创建一个DatagramSocket
对象,但是通常不用绑定特定端口;
生成请求并打包成DatagramPacket
数据报发送给服务器。 - 接收响应并解析
接收来自服务器响应(如果有),将接收到的来自服务器的响应解析并显示出来。 - 根据响应作出进行下一步操作
如将响应显示出来。
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
// 创建一个socket对象用于发送和接收数据报
socket = new DatagramSocket();
this.serverIp = serverIp;
this.serverPort= serverPort;
}
public void start() throws IOException {
Scanner scan = new Scanner(System.in);
System.out.println("客户端启动");
while(true) {
// 1. 生成请求并发送
System.out.print("输入请求:");
String request = scan.next();
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),0,request.getBytes().length,
InetAddress.getByName(serverIp), serverPort);
socket.send(requestPacket);
// 2. 接收来自服务器返回的响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
// 3. 解析响应并显示
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
}
```