上一篇博客中使用的Socket是基于TCP协议的,这一篇为大家简单介绍一下UDP协议。
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。
UDP协议全称是用户数据报协议[1] ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。
与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
TCP与UDP的区别:
1. 基于连接与无连接;
2. 对系统资源的要求(TCP较多,UDP少);
3. UDP程序结构较简单;
4. 流模式与数据报模式 ;
5. TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
DatagramSocket
此类表示用来发送和接收数据报包的套接字。数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。
示例:DatagramSocket s = new DatagramSocket(null); s.bind(new InetSocketAddress(8888)); 这等价于:DatagramSocket s = new DatagramSocket(8888); 两个例子都能创建能够在 UDP 8888 端口上接收广播的 DatagramSocket。
构造方法
方法名 | 说明 |
---|---|
DatagramSocket() | 构造数据报套接字并将其绑定到本地主机上任何可用的端口 |
DatagramSocket(int port) | 创建数据报套接字并将其绑定到本地主机上的指定端口 |
DatagramSocket(int port, InetAddress laddr) | 创建数据报套接字,将其绑定到指定的本地地址 |
DatagramSocket(SocketAddress bindaddr) | 创建数据报套接字,将其绑定到指定的本地套接字地址 |
方法摘要
返回值 | 方法名 | 说明 |
---|---|---|
void bind(SocketAddress addr) | 将此 DatagramSocket 绑定到特定的地址和端口 | |
void close() | 关闭此数据报套接字 | |
void connect(InetAddress address, int port) | 将套接字连接到此套接字的远程地址 | |
void connect(SocketAddress addr) | 将此套接字连接到远程套接字地址(IP 地址 + 端口号) | |
void disconnect() | 断开套接字的连接 | |
boolean getBroadcast() | 检测是否启用了 SO_BROADCAST | |
DatagramChannel getChannel() | 返回与此数据报套接字关联的唯一 DatagramChannel 对象(如果有) | |
InetAddress getInetAddress() | 返回此套接字连接的地址 | |
InetAddress | getLocalAddress() | 获取套接字绑定的本地地址 |
int | getLocalPort() | 返回此套接字绑定的本地主机上的端口号 |
SocketAddress | getLocalSocketAddress() | 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null |
int | getPort() | 返回此套接字的端口 |
int | getReceiveBufferSize() | 获取此 DatagramSocket 的 SO_RCVBUF 选项的值,该值是平台在 DatagramSocket 上输入时使用的缓冲区大小 |
SocketAddress | getRemoteSocketAddress() | 返回此套接字连接的端点的地址,如果未连接则返回 null |
boolean | getReuseAddress() | 检测是否启用了 SO_REUSEADDR |
int | getSendBufferSize() | 获取此 DatagramSocket 的 SO_SNDBUF 选项的值,该值是平台在 DatagramSocket 上输出时使用的缓冲区大小 |
int | getSoTimeout() | 获取 SO_TIMEOUT 的设置 |
int | getTrafficClass() | 为从此 DatagramSocket 上发送的包获取 IP 数据报头中的流量类别或服务类型 |
boolean | isBound() | 返回套接字的绑定状态 |
boolean | isClosed() | 返回是否关闭了套接字 |
boolean | isConnected() | 返回套接字的连接状态 |
void | receive(DatagramPacket p) | 从此套接字接收数据报包 |
void | send(DatagramPacket p) | 从此套接字发送数据报包 |
void | setBroadcast(boolean on) | 启用/禁用 SO_BROADCAST |
static void | setDatagramSocketImplFactory(DatagramSocketImplFactory fac) | 为应用程序设置数据报套接字实现工厂 |
void | setReceiveBufferSize(int size) | 将此 DatagramSocket 的 SO_RCVBUF 选项设置为指定的值 |
void | setReuseAddress(boolean on) | 启用/禁用 SO_REUSEADDR 套接字选项 |
void | setSendBufferSize(int size) | 将此 DatagramSocket 的 SO_SNDBUF 选项设置为指定的值 |
void | setSoTimeout(int timeout) | 启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位 |
void | setTrafficClass(int tc) | 为从此 DatagramSocket 上发送的数据报在 IP 数据报头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。 |
DatagramPacket
此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
构造方法
方法名 | 说明 |
---|---|
DatagramPacket(byte[] buf, int length) | 构造 DatagramPacket,用来接收长度为 length 的数据包 |
DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号 |
DatagramPacket(byte[] buf, int offset, int length) | 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量 |
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) | 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号 |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号 |
DatagramPacket(byte[] buf, int length, SocketAddress address) | 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号 |
方法摘要
返回值 | 方法名 | 说明 |
---|---|---|
InetAddress | getAddress() | 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的 |
byte[] | getData() | 返回数据缓冲区 |
int | getLength() | 返回将要发送或接收到的数据的长度 |
int | getOffset() | 返回将要发送或接收到的数据的偏移量 |
int | getPort() | 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的 |
SocketAddress | getSocketAddress() | 获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号) |
void | setAddress(InetAddress iaddr) | 设置要将此数据报发往的那台机器的 IP 地址 |
void | setData(byte[] buf) | 为此包设置数据缓冲区 |
void | setData(byte[] buf, int offset, int length) | 为此包设置数据缓冲区 |
void | setLength(int length) | 为此包设置长度 |
void | setPort(int iport) | 设置要将此数据报发往的远程主机上的端口号 |
void | setSocketAddress(SocketAddress address) | 设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号) |
使用示例
服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP服务端
*
* @author jianggujin
*
*/
public class UDPServer
{
public static void main(String[] args) throws IOException
{
DatagramSocket server = new DatagramSocket(9999);
// 用于接收数据的缓冲数组
byte[] recvBuf = new byte[100];
// 实例化数据报对象
DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
// 接收消息
server.receive(recvPacket);
String recvStr = new String(recvPacket.getData(), 0,
recvPacket.getLength());
System.out.println("接收到消息:" + recvStr);
int port = recvPacket.getPort();
InetAddress addr = recvPacket.getAddress();
String sendStr = "SUCCESS";
byte[] sendBuf = sendStr.getBytes();
// 创建回复数据报
DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length,
addr, port);
// 发送回复
server.send(sendPacket);
server.close();
}
}
客户端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP客户端
*
* @author jianggujin
*
*/
public class UDPClient
{
public static void main(String[] args) throws IOException
{
DatagramSocket client = new DatagramSocket();
String sendStr = "HI!";
byte[] sendBuf = sendStr.getBytes();
// 接收消息的主机,255.255.255.255可广播给局域网内所有主机
InetAddress addr = InetAddress.getByName("127.0.0.1");
DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length,
addr, 9999);
// 发送数据包
client.send(sendPacket);
// 用于接收数据的缓冲数组
byte[] recvBuf = new byte[100];
// 实例化数据报对象
DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
// 接收消息
client.receive(recvPacket);
String recvStr = new String(recvPacket.getData(), 0,
recvPacket.getLength());
System.out.println("收到消息:" + recvStr);
client.close();
}
}
运行结果
服务端:
接收到消息:HI!
客户端:
收到消息:SUCCESS