上一篇博客中使用的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();
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