资讯专栏INFORMATION COLUMN

Java 网络编程(2):UDP 的使用

learn_shifeng / 2660人阅读

摘要:现在在本机同一局域网的一台机器和阿里云主机上都运行然后启动发送端接收端接收结果可以看到每个接收端都正确的接收了发送端发送的消息。

今天的主角是 UDP(User Datagram Protocol,用户数据报协议)。
我们都知道 TCP 是一种可靠的协议 —— 首先客户端和服务端需要建立连接(三次“握手”),数据发送完毕需要断开连接(四次“挥手”);如果发送数据时数据损坏或者丢失,那么 TCP 会重新发送。保证可靠的代价就是效率的降低(建立连接和断开连接就需要时间,保证数据的可靠性也需要额外的消耗)。与 TCP 相对应,UDP 是面向无连接的协议,并且它不保证数据是否会到达,也不保证到达的数据是否准确和数据顺序是否正确 —— 所以相比于 TCP, UDP 的速度很快。在 需要不建立连接即可发送数据 的系统,或者 保证最快的传输速度比每一位数据都正确更重要 的系统(如视频会议,丢失某个数据包只是一个画面或者声音的小干扰)中,UDP 才是正确的选择。实际上,在同一个网段,或者在信号很好的局域网,UDP 是非常可靠的。

在 Java 中使用 UDP 时关键的两个类分别是:DatagramSocketDatagramPacket
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 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 messages)
            throws IOException, InterruptedException {

        // 无参构造的 DatagramSocket 会随机选择一个端口进行监听
        // 因为此时 DatagramSocket 的作用是发送,所以无需显式指定固定端口
        try (DatagramSocket socket = new DatagramSocket()) {
            System.out.println("随机给发送端分配的端口为:" + socket.getLocalPort() + "
");
            
            for (DatagramPacket msg : messages) {
                socket.send(msg); // 发送数据包

                int recverPort = msg.getPort();
                InetAddress recverAddr = msg.getAddress();
                System.out.printf("消息已经发送 -> (%s:%d)
",
                        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("接收端已经启动...
");
            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)
",
                        senderAddr.getHostAddress(), senderPort);

                System.out.println("发送端 发送的消息 -> " + msgBody + "
");
            }
        }

        System.out.println("接收端已经关闭。");
    }
}

现在 在本机、同一局域网的一台机器和阿里云主机上都运行 UDPReceiver:

然后启动发送端 UDPSender:

接收端接收结果:

可以看到每个接收端都正确的接收了发送端发送的消息。

(本机 和 局域网机器 显示的发送端的端口(49756)是一致的,但是远程机器即阿里云主机显示的(4802)却与他们不一致——这个问题留给有兴趣的读者自己思考)

虽然 UDP 是不可靠的协议,但是因为它不需要建立连接,效率更快,所以在 需要不建立连接即可发送数据 的系统(比如本文的例子),或者 保证最快的传输速度比每一位数据都正确更重要 的系统中,我们应该使用 UDP。当然,基于 UDP 我们同样可以开发出可靠的协议——数据包的正确与否可以交给应用程序来判断,如果有问题接收端便提示发送端重新发送。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69833.html

相关文章

  • Java015-网络编程

    摘要:具备发送和接受功能,在进行传输时,需要明确一个是发送端,一个是接收端。指定发送端口,不指定系统会随机分配。传输两个端点的建立连接后会有一个传输数据的通道,这通道称为流,而且是建立在网络基础上的流,称之为流。 端口:物理端口:逻辑端口: 用于标识进程的逻辑地址,不同进程的标识;有效端口:0~65535,其中0~1024系统使用或保留端口。 java 中ip对象:InetAddress....

    Blackjun 评论0 收藏0
  • JAVA网络程序设计基础(笔记)

    摘要:三端口与套接字端口指一台计算机只有单一的连接到网络的物理连接,所以的数据都通过此连接对内对外送达特定的计算机,这就是端口。三程序设计由上面可知基于的信息传递速度更快。接收数据包使用创建数据包套接字,绑定指定端口。 服务器 网络 客户机 第一部分 一.局域网与因特网 服务器是指提供信息的计算机或程序,...

    PAMPANG 评论0 收藏0
  • Java网络编程(3):使用 UDP 探测局域网内特定类型机器

    摘要:那没有建立连接的情况下,发现房间这个功能是怎么实现的呢首先,既然手机处于局域网中,那么根据手机当前在局域网的地址和子网掩码,就可以获得这个局域网内所有机器的地址的范围。 记得以前我们使用类似快牙这些文件分享工具的时候,一开始就是先在 手机A 上创建一个房间,然后连接上 手机A WiFi 热点的其他手机(即这些手机处于一个局域网内)就可以发现到这个房间并加入到这个房间里面,然后就可以互相...

    focusj 评论0 收藏0
  • Java Socket编程UDP编程

    摘要:更多资料请看编程之编程协议用户数据报协议是无连接的不可靠的无序的速度快进行数据传输时,首先将要传输的数据定义成数据报,大小限制在,在数据报中指明数据索要达到的主机地址和端口号,然后再将数据报发送出去类表示数据报包类进行端到端通信的类服务器端 更多资料请看:https://www.yuque.com/shizhiy... Java Socket编程之UDP编程 UDP协议(用户数据报协...

    ityouknow 评论0 收藏0
  • 1、网络三要素及传输协议 2、实现UDP协议发送端和接收端 3、实现TCP协议客户端和服务器 4

    摘要:应用层主要负责应用程序的协议,例如协议协议等。在计算机中,不同的应用程序是通过端口号区分的。区别在于,中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。 01网络模型 *A:网络模型 TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。 链路层:链路层是用于定义物理传输通道,通常是对...

    CastlePeaK 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<