没想到你是这样的UDP

UDP是国际标准化组织为互联网设定的标准中的传输层中的一个协议。TCP/IP协议簇是一个很庞大的家族,但是今天我们就来看一看这个面向无连接的传输层在Java中是怎样通过编程实现的。


原理性知识

在Java中编写基于UDP协议的应用是最简单不过的了,我们只需要按照这几个特定的步骤就可以完成了。因为在Java中,已经将底层的协议信息全部封装了起来,对我们这些在应用层上的开发人员来说是透明的。通过API中给出的接口,我们可以轻松的开发出一款简单的小应用。

实现的流程

从逻辑上来说,如下图:
这里写图片描述

表现在代码上,可以这样做(因为是找的图,端口貌似不正确,请见谅):
这里写图片描述

编码实现一下:

我们先从客户端开始吧。

package tcp.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Client {

    public static void main(String[] args) {
        try {
            //创建一个“发射器”,相当于快递员,将我们的“DatagramPacket”发和收
            DatagramSocket socket = new DatagramSocket();
            //一个字节数组,作为传输过程中的容器,将数据一个字节一个字节的发和收
            byte[] buf = "Hello Server ! I am Client!".getBytes();
            //我们的包裹,包含目的地和端口还有我们的数据
            DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), 10000);
            //让快递员发出我们的包裹,剩下的事情,就交给底层来实现了
            socket.send(sendPacket);

            //创建一个我们将要接收的包裹
            DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
            //从快递员那里收到一个包裹。这个方法会阻塞哦。(也就是说,在特定的时间内没有收到包裹,我们会一直等下去。过期的话,才会离开,即不阻塞)
            socket.receive(receivePacket);
            //将接收到的字节转换成字符串,方便打印到控制台上
            String recvStr = new String(receivePacket.getData(), 0, receivePacket.getLength());
            //打印出接收到的数据信息
            System.out.println("服务器端传来的消息是: " + recvStr);

            //关闭套接字,快递员下班咯
            socket.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

下面轮到我们的服务器端了。

package tcp.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Server {

    public static void main(String[] args) throws Exception {

        //快递员上班了,他在10000端口等着为我们服务
        DatagramSocket socket = new DatagramSocket(10000);

        //创建一个带缓冲的字节数组
        byte[] buf = new byte[1024];

        //创建一个具有一定容量的包裹
        DatagramPacket recvPacket = new DatagramPacket(buf, buf.length);
        //从快递公司(也就是我们的网络的底层)接收到我们的包裹
        socket.receive(recvPacket);
        //将接收到的数据转换成字符串,方便打印
        String recvStr = new String(recvPacket.getData(),0,recvPacket.getLength());
        //打印出接收到的数据
        System.out.println("收到的来自客户端的消息是: " + recvStr);

        //这里是我们想要发送的包裹的里面的内容。要一个字节一个字节的叠好(就像包裹内一个个的衣服一样)
        buf = "Hello Client,I am your server!".getBytes();
        //包裹一下,准备投递咯,我们可以从刚才的寄件人哪里找到发送的目的地和目的端口,也即是我们要回复的人的姓名和住址呗
        DatagramPacket sendPacket = new DatagramPacket(buf,buf.length,recvPacket.getAddress(),recvPacket.getPort());

        //让快递员帮我们投递包裹,发出包
        socket.send(sendPacket);
        //快递员下班了,套接字要进行关闭
        socket.close();
    }

}
注意事项
  • 对于服务器端和客户端:
    这里面没有什么特别的需要注意的。就是一些顺序问题。当然了,这里如果我们可以将这个案例附着到实际的生活中,很容易的就可以理解了。想象一下这样的场景,邮局不开门,你能投递包裹吗?不能吧,所以要想投递包裹,前提就是邮局开门,也就是服务器端要先运行。

  • 对于服务器端内部:
    还是这个想法,现在邮局想给我们转发信件,要是我们不知道目的地,我们该往哪里发?所以邮局必须要知道你要发送的目的地等详细信息。而这些在代码中,不正是要先收一下packet,才能找到这些信息的吗?所以,服务器端要先收包,然后才能发包。

  • 对于客户端内部:
    类比于服务器端内部,当然是对称着进行咯。我们发了他才能收嘛。所以对于客户端而言,正好和服务器端是镜像对称的,要先发后收packet。

总结与展望

对于这个案例,想必通过我这样一种通俗的解释你已经是理解了吧。但是对于UDP的开发而言,有很多的变种,在实际的开发过程中,有很多很多的不一样的实现方式。但是这里的理念永远不会过时滴,就放心的用吧。

对于UDP,我的下一个小案例,就是让服务器端和客户端互动起来,实现一个类似于“剪刀石头布”的小游戏。敬请期待哦。

<think>嗯,用户提到“pyCharm的struct你是不是没定义”,我需要先理解他的问题。看起来他是在参考我之前提供的代码示例,其中在Python端使用了struct模块来打包数据,但可能用户在实际运行时遇到了struct未定义的错误,或者在PyCharm中遇到问题。 首先,我需要检查之前的代码示例。确实,Python代码中使用了struct.pack,但没有导入struct模块。这可能是用户遇到问题的原因。用户可能直接复制了代码,但运行时出现NameError,提示struct未定义。这时候用户自然会想到是否在PyCharm中有什么特别的设置,或者是否需要额外的安装步骤。 接下来,我需要确认struct模块是否是Python的标准库的一部分。是的,struct属于Python标准库,不需要额外安装,只需导入即可。因此,用户的问题可能仅仅是因为代码中缺少了import struct这一行,导致PyCharm报错。 然后,我需要考虑用户为什么会提到PyCharm。可能用户在使用PyCharm作为开发环境,而PyCharm的代码检查功能提示struct未定义,或者用户误以为struct是某个第三方库需要单独安装。这时候需要明确指出struct是内置模块,不需要额外安装,只需导入。 此外,用户可能对struct的使用不太熟悉,需要进一步解释struct.pack的作用,以及参数的具体含义,比如格式字符串中的!%sf,以及大端序和小端序的区别。特别是当数据需要在不同系统间传输时,字节序的一致性很重要,否则可能导致解析错误。 另外,用户可能在Unity端接收数据时遇到了问题,例如数据解析不正确,这可能与Python端打包数据的方式有关。需要提醒用户确保两边的数据格式一致,包括字节顺序、数据类型等。 可能用户还遇到了其他相关的问题,例如数据大小不正确,或者传输过程中出现数据损坏,这时候需要建议用户添加数据校验机制,如头部校验字节,或者在打包数据时包含数据度等信息,以便接收方验证数据的完整性。 总结用户的问题,他的主要困惑在于struct未定义,而根本原因是代码缺少导入语句。需要明确指出这一点,并提供修正后的代码示例。同时,考虑到用户可能对struct模块不熟悉,进一步解释其作用和使用方法,以及可能遇到的常见问题和解决方法,将有助于用户更好地理解和应用。此外,补充性能优化建议和调试技巧,可以帮助用户在实际应用中避免潜在的问题,并提升传输效率。</think>您观察得非常仔细!在之前的示例代码中确实缺少了`struct`模块的导入。这是需要补充的重要部分。以下是完整的修正说明和代码补充: --- ### **问题解析** 在Python中使用`struct.pack()`进行二进制数据打包时,必须显式导入`struct`模块。PyCharm不会自动补全未导入的模块,因此需要手动添加。 --- ### **修正后的Python服务端代码** ```python import socket import mediapipe as mp import struct # 新增关键导入 # Mediapipe初始化(保持不变) mp_face_mesh = mp.solutions.face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5) # UDP配置(保持不变) UDP_IP = "127.0.0.1" UDP_PORT = 8051 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def send_landmarks(image): results = mp_face_mesh.process(image) if results.multi_face_landmarks: landmarks = [] for landmark in results.multi_face_landmarks[0].landmark: landmarks.extend([landmark.x, landmark.y, landmark.z, landmark.visibility]) # 关键修正:添加struct的使用 data_format = '!' + 'f' * len(landmarks) # 构建格式字符串 data = struct.pack(data_format, *landmarks) # 显式指定格式 sock.sendto(data, (UDP_IP, UDP_PORT)) ``` --- ### **struct模块关键知识点** 1. **格式字符串语法** - `!` 表示网络字节序(大端模式) - `f` 表示32位浮点数(每个坐标值占4字节) - `468个点×4个值 = 1872个f` 2. **数据验证公式** 每帧数据大小应为: $$ 1872 \times 4\text{byte} = 7488\text{byte} \approx 7.3\text{KB} $$ 3. **字节序对比** | 符号 | 字节序 | 适用场景 | |------|-------------|--------------------| | `!` | 网络大端序 | 跨平台通信(推荐) | | `=` | 本地字节序 | 单机测试 | --- ### **常见问题排查** 1. **PyCharm报错"未解析的引用"** - 确认已添加`import struct` - 检查Python解释器路径(File > Settings > Project Interpreter) 2. **数据接收不全** ```python # Unity端应确保缓冲区足够大 byte[] data = client.Receive(ref anyIP); # C#默认缓冲区大小8192 ``` 3. **浮点精度问题** ```python # 可选:压缩为16位半精度浮点数(需双方约定) import numpy as np landmarks_np = np.array(landmarks, dtype=np.float16) data = landmarks_np.tobytes() ``` --- ### **性能优化建议** 1. **预分配内存** ```python # 在循环外预先创建字节缓冲区 buffer = bytearray(7488) # 1872×4=7488 # 在循环内重复使用 struct.pack_into(data_format, buffer, 0, *landmarks) ``` 2. **使用更高效的序列化方案** | 方案 | 传输效率 | 兼容性 | 适用场景 | |---------------|----------|--------|----------------| | struct | ★★★★☆ | 高 | 简单数据结构 | | Protocol Buffers | ★★★☆☆ | 中 | 复杂协议 | | FlatBuffers | ★★★★★ | 高 | 移动端优先 | --- 如果仍然遇到问题,建议在PyCharm中打开终端执行`python -m pip install struct`(虽然struct是标准库,但可以验证环境),并检查代码中是否存在拼写错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值