一、简介
与 TCP 不同,UDP 是一个面向数据包的传输层协议,进程的每一个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。格式如下:
IP数据报的最大长度为 65535 字节 ,除去首字IP 的20 字节和 UDP首部8个字节,实际上,UDP 能传输的最大字节数为 65507个字节;当我们的数据超过这个长度时,则需要考虑分包的问题。
UDP 的传输是不可靠的,它只负责把数据传输出去,并不会去考虑接收端是否能接受到。在大多不需要考虑应答的应用中,我们会优先考虑 UDP
二、DatagramSocket 和 DatagramPacket
socket 的 udp 的 api 是通过 DatagramSocket 和 DatagramPacket 来实现的。
我们知道两台计算机的通信,无论是 TCP 还是 UDP ,都需要知道 IP 和 端口。
2.1 DatagramPacket类:数据报文
(1)定义概念
此类表示数据报包。
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
(2)构造方法
构造函数名称 | 含义 |
---|---|
DatagramPacket(byte[] buf, int length) | 接收构造函数, 用来接收长度为 length 的数据包 |
DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 发送构造函数,构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号 |
(3)主要方法
- getAddress()返回接收或发送此数据报文的机器的 IP 地址。
- getData()返回接收的数据或发送出的数据。
- getLength()返回发送出的或接收到的数据的长度。
- getPort()返回接收或发送该数据报文的远程主机端口号。
2.2 DatagramSocket类:数据报套接字
(1)定义概念
此类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。
(2)构造函数总结
构造函数名称 含义
DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。
(3)重要方法摘要
方法名称 | 含义 |
---|---|
void close() | 关闭此数据报套接字。 |
void connect(InetAddress address, int port) | 将套接字连接到此套接字的远程地址。 |
boolean isClosed() | 返回是否关闭了套接字。 |
void receive(DatagramPacket p) | 从此套接字接收数据报包。 |
void send(DatagramPacket p) | 从此套接字发送数据报包。 |
DatagramSocket(int port, InetAddress laddr) | 创建数据报套接字,将其绑定到指定的本地地址。 |
2.3 InetAddress类
(1)定义概念
此类表示互联网协议 (IP) 地址。
IP 地址是 IP 使用的 32 位或 128 位无符号数字,它是一种低级协议,UDP 和 TCP 协议都是在它的基础上构建的。InetAddress 的实例包含 IP 地址,还可能包含相应的主机名(取决于它是否用主机名构造或者是否已执行反向主机名解析)。
(2)创建方法
注意,创建此类事通过类方法而获取,并非构造方法。
方法名称 | 含义 |
---|---|
static InetAddress getByAddress(byte[] addr) | 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 |
static InetAddress getByAddress(String host, byte[] addr) | 根据提供的主机名和 IP 地址创建 InetAddress。 |
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址。 |
三、发送、接收UDP报文步骤讲解
- 同一端如果既要发送消息又想接收消息,一定要将发送的socket和接收的socket分开。不能用一个socket既接收信息,又发送信息,这样一定会造成阻塞。
- 接收线程中,一定要有 while(true){ if(标志何时结束) 代码块 } 这样的方式,不然,在接收信息时,一定会造成阻塞,就是只能接收到一次信息。
3.1 数据报发送解析
以下步骤都在发送线程内,需要注意的是发送UDP报文逻辑单一,顺序执行完毕线程即可结束,不涉及到后台等待的需求,所以执行完后即可关闭套接字连接。
发送步骤
- 构造DatagramSocket对象
- 根据发送IP 来创建InetAddress对象
- 根据InetAddress对象、发送端口号、发送数据 来创建发送的DatagramPacket数据包对象
- 调用DatagramSocket对象的send(datagramPacket) 方法,发送UDP报文
- 调用DatagramSocket对象的close() 关闭套接字连接
对应以上步骤,代码展示(仅为部分重要代码):
Byte[] buf="hello android! ".getBytes();
DatagramSocket sendSocket = new DatagramSocket();
InetAddress serverAddr = InetAddress.getByName(SEND_IP);
DatagramPacket outPacket = new DatagramPacket(buf, buf.length,serverAddr, SEND_PORT);
sendSocket.send(outPacket);
sendSocket.close();
SEND_IP即是你想要发送的地址,SEND_PORT端口号则要选用闲置端口就是向8000~9000这样的端口号。
3.2 数据报接收 解析
原理
以下步骤都在接收线程内,同发送线程大致相同,但需要注意这两者本质的区别:发送线程里的逻辑执行一遍即可结束,但是接收线程需要在后台待定等待接收UDP报文,不可执行一遍就结束!相当于在一个界面中,可多次创建发送线程用来发送报文,但是接收线程只需在界面初始化时创建,从而一直监听报文接收(若重新进入界面,逻辑如上)。
所以,在接收线程内部需要用到循环&