脚本宝典收集整理的这篇文章主要介绍了Java 网络编程(2):UDP 的使用,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
今天的主角是 UDP(User Datagram PRotocol,用户数据报协议)。
我们都知道 TCP 是一种可靠的协议 —— 首先客户端和服务端需要建立连接(三次“握手”),数据发送完毕需要断开连接(四次“挥手”);如果发送数据时数据损坏或者丢失,那么 TCP 会重新发送。保证可靠的代价就是效率的降低(建立连接和断开连接就需要时间,保证数据的可靠性也需要额外的消耗)。与 TCP 相对应,UDP 是面向无连接的协议,并且它不保证数据是否会到达,也不保证到达的数据是否准确和数据顺序是否正确 —— 所以相比于 TCP, UDP 的速度很快。在 需要不建立连接即可发送数据 的系统,或者 保证最快的传输速度比每一位数据都正确更重要 的系统(如视频会议,丢失某个数据包只是一个画面或者声音的小干扰)中,UDP 才是正确的选择。实际上,在同一个网段,或者在信号很好的局域网,UDP 是非常可靠的。
在 Java 中使用 UDP 时关键的两个类分别是:DatagramSocket 和 DatagramPacket。
DatagramPacket 表示数据包,而 DatagrameSocket 用来发送和接收数据包。
- 发送数据包时:
因为 UDP 协议并不需要建立连接,所以我们将数据(byte数组)放入 DatagramPacket 之后,还需要将目的地(IP地址和端口)放入到 DatagramPacket 中 —— DatagramSocket 的 send(DatagramPacket packet) 方法根据 packet 中指定目的地,将其包含的数据往这个目的地发送。至于数据是否能(准确)到达目的地,DatagramSocket 并不关心。
- 接收数据包时:
DatagramSocket 在接收数据包时,我们需要为其指定一个监听的端口。当有包含了接收端机器 IP 地址和 DatagramSocket 所监听端口的数据包到达时,DatagramSocket 的 receive(DatagramPacket packet) 方法便会对数据包进行接收,并将接收到的数据包填入到 packet 中。
现在让我们来实践 DatagramSocket 和 DatagramPacket:
因为 UDP 没有服务端和客户端之分,所以我们把两端分别定义为 发送端 和 接收端。
发送端代码:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UDPSender { private static final int RECVER_LISTENING_PORT = 9999; public static void main(String[] args) throws Exception { List<DatagramPacket> messages = new ArrayList<>(12); for (int i = 0; i < 4; i++) { String msgBody = (i == 3 ? "" : "Hello UDP-" + i); DatagramPacket msg0 = parseMsg( msgBody, "127.0.0.1", RECVER_LISTENING_PORT); // 发送给本机 DatagramPacket msg1 = parseMsg( msgBody, "192.168.3.3", RECVER_LISTENING_PORT); // 发送给同一局域网的一台机器 DatagramPacket msg2 = parseMsg( msgBody, "120.77.**.***", RECVER_LISTENING_PORT); // 120.77.**.*** 是我阿里云主机的公网 IP 地址 // JDK1.5 时 Collections 添加的 addAll 方法,可以一次往某个集合中添加多个元素 Collections.addAll(messages, msg0, msg2, msg1); } startSending(messages); } private static void startSending(List<DatagramPacket> messages) throws IOException, InterruptedException { // 无参构造的 DatagramSocket 会随机选择一个端口进行监听 // 因为此时 DatagramSocket 的作用是发送,所以无需显式指定固定端口 try (DatagramSocket socket = new DatagramSocket()) { System.out.println("随机给发送端分配的端口为:" + socket.getLocalPort() + "n"); for (DatagramPacket msg : messages) { socket.send(msg); // 发送数据包 int recverPort = msg.getPort(); InetAddress recverAddr = msg.getAddress(); System.out.printf("消息已经发送 -> (%s:%d)n", recverAddr.getHostAddress(), recverPort); Thread.sleep(500); // 设定 每隔 0.5 秒发送一个消息 } } } private static DatagramPacket parseMsg(String msgBody, String addr, int port) throws UnknownHostException { byte[] msgData = msgBody.getBytes(); DatagramPacket msg = new DatagramPacket( msgData, 0, msgData.length, // 数据从位置 0 开始,长度为 msgData.length InetAddress.getByName(addr), port); // 目的地 地址为 addr,监听端口为 port return msg; } }
接收端代码:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPReceiver { private static final int LISTENING_PORT = 9999; private static final int BUFFER_SIZE = 512; public static void main(String[] args) throws Exception { byte[] buffer = new byte[BUFFER_SIZE]; DatagramPacket msg = new DatagramPacket(buffer, buffer.length); try (DatagramSocket socket = new DatagramSocket(LISTENING_PORT)) { System.out.println("接收端已经启动...n"); while (true) { socket.receive(msg); // 接收数据包 String msgBody = new String( msg.getData(), msg.getOffset(), msg.getLength()); if (msgBody.isEmpty()) { // 发现接收的消息是空字符串("")便跳出循环 break; } int senderPort = msg.getPort(); InetAddress senderAddr = msg.getAddress(); System.out.printf("发送端 地址和端口 -> (%s:%d)n", senderAddr.getHostAddress(), senderPort); System.out.println("发送端 发送的消息 -> " + msgBody + "n"); } } System.out.println("接收端已经关闭。"); } }
现在 在本机、同一局域网的一台机器和阿里云主机上都运行 UDPReceiver:
@H_716_304@
然后启动发送端 UDPSender:
接收端接收结果:
可以看到每个接收端都正确的接收了发送端发送的消息。
(本机 和 局域网机器 显示的发送端的端口(49756)是一致的,但是远程机器即阿里云主机显示的(4802)却与他们不一致——这个问题留给有兴趣的读者自己思考)
虽然 UDP 是不可靠的协议,但是因为它不需要建立连接,效率更快,所以在 需要不建立连接即可发送数据 的系统(比如本文的例子),或者 保证最快的传输速度比每一位数据都正确更重要 的系统中,我们应该使用 UDP。当然,基于 UDP 我们同样可以开发出可靠的协议——数据包的正确与否可以交给应用程序来判断,如果有问题接收端便提示发送端重新发送。
以上是脚本宝典为你收集整理的Java 网络编程(2):UDP 的使用全部内容,希望文章能够帮你解决Java 网络编程(2):UDP 的使用所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。