【问题标题】:UDP client does not receive bytesUDP 客户端不接收字节
【发布时间】:2023-09-26 12:18:01
【问题描述】:

这个问题被问了很多,但到目前为止,我从以前的答案中应用的解决方案都没有帮助我。

主要目标

我正在尝试学习 UDP conexions,这是我的尝试。我想让客户端通过 UDP 在服务器上请求图片,服务器将发送它。然后客户端将创建一个包含该信息的文件。

说明

我的主要想法是使用“GET”命令(不是 HTTP,只是 GET)向服务器请求图像,然后是图像的名称(包括扩展名)。然后客户端等待响应,即请求的图像。

问题

客户端等待并回答没有来

研究

  • 从另一个类似的问题来看,我使用相同的端口进行接收和连接是一个问题,所以我添加了两个端口,接收端口和发送端口,客户端没有结果。

  • 从其他类似的问题来看,这是一个防火墙问题。因此,在 Win10 机器上,我在防火墙中为我用于此应用程序的端口创建了一个新的 UDP 规则,但客户端没有收到任何内容...

我已检查图像是否已加载到byte[] 并已发送。但是在客户端上,什么都没有收到并停留在那里等待连接通过

来自服务器的代码

public class UDPserver {

    static DatagramSocket serverUDP;
    static DatagramPacket packet;
    static InetAddress address;
    static byte[] buffer = new byte[65507];//65507
    final static int receivingPORT = 6668;
    final static int sendingPORT = 6669; 

    public static void main(String[] args) throws SocketException, IOException, InterruptedException{

        boolean serverActive = true;

        String order = "";
        String file = "";

        //Instantiate server
        serverUDP = new DatagramSocket(receivingPORT);

        while(serverActive){            

            //Kind of packet we want to receive
            packet = new DatagramPacket(buffer, buffer.length);

            System.out.println("Server awaiting connection...");
            //Receive it
            serverUDP.receive(packet);
            System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());

            //What does the packet contain?
            String msg = new String(packet.getData());
            address = packet.getAddress();
            System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);

            try{
                order = msg.split(" ")[0].trim();
                file = msg.split(" ")[1].trim();
            } catch (Exception e){

            }           

            switch(order){
                case("GET"):{
                    System.out.println("Sending back an image...");
                    buffer = loadImageFromServer(file);
                    packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
                    Thread.sleep(5000);
                    serverUDP.send(packet); 
                    System.out.println("Client served");
                    break;
                }
                case("DISCONNECT"):{
                    buffer = "Server is disconnecting...".getBytes();
                    packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
                    serverUDP.send(packet);
                    serverActive = false;
                    serverUDP.close();
                    break;
                }                
            }                   
        }        
    }

    static byte[] loadImageFromServer(String path) {

        try {
            System.out.println("Loading path: " + path);
            //Instantiate a buffer from the image for it
            BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
            //Create a byte[] stream object to handle the data
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //Write the image data into those above with jpg format
            ImageIO.write(img, "png", baos);
            //Flush the information
            baos.flush();

            byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
            return buffer;            

        } catch (IOException ex) {
            Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
            System.exit(-1);
        }
        return null;
    }
}

CODE 客户端

public class Client {

    static DatagramSocket clientUDP;
    static InetAddress address;
    static DatagramPacket packetSend;
    static DatagramPacket packetReceive;

    static int SIZE = 65507;
    final static int receivingPORT = 6669;
    final static int sendingPORT = 6668; 

    static byte[] buffer = new byte[SIZE];

    static Scanner scan = new Scanner(System.in);

    public static void main(String[] args) throws SocketException, UnknownHostException, IOException{


            boolean clientLoop = true;

            //Get address
            address = InetAddress.getByName("localhost");

            //Instantiate Client -> UDP
            clientUDP = new DatagramSocket();

            while(clientLoop){
                System.out.print("Enter any key and press enter");
                scan.next(); //Just to stop the loop

                //Load the buffer
                buffer = "GET imagenServidor.png".getBytes();
                //buffer = "DISCONNECT".getBytes();

                System.out.println("Buffer is ready");

                //Arm the packet
                packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
                System.out.println("Packet is armed!");

                //Send the packet to the server
                clientUDP.send(packetSend);
                System.out.println("Order sent to server");

                System.out.println("Waiting an answer");

                packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
                clientUDP.receive(packetReceive);
                System.out.println("Server answered!");

                ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
                BufferedImage image = ImageIO.read(bais);
                System.out.println(image);
            }            
            clientUDP.close();
    }    
}

注意事项

  • 这是一个 UDP 练习

【问题讨论】:

  • 1.没有UDP连接之类的东西。 2、发送和接收使用同一个端口是没有问题的,不要这样浪费端口。 3. 您需要ew ByteArrayInputStream(packetReceive.getData(), 0, packetReceive.getLength()); 4. 最重要的是,您可能未能发送 65507 字节的数据报。首先,您不能发送大于套接字发送缓冲区的数据报,并且在任何情况下,这实际上对于 UDP 来说太大了,对于大多数图像来说也太小了。基本上你不能这样做。使用 TCP。
  • 我知道 TCP 可以完美地解决我的问题,但这是我必须理解的功课。我将尝试应用您的观点。谢谢
  • 因为您必须使用 UDP 进行练习,所以您需要创建一个应用层协议来处理将流转换为比数据流小得多的单个消息。如果您使用大约 576 字节的数据大小,UDP 效果最好。实时协议,例如VoIP,在 UDP 上使用小得多(20 字节左右)的数据大小,因为 UDP 是无连接的,数据包会丢失。您需要考虑到这一点,并且您的协议必须重新请求传输中丢失的数据。
  • 好评@RonMaupin。这将是完善分配的一个很好的补充,尽管现在我只是学习基础知识。我有一个解决方案,我将在之后发布一个我处理 Netbeans 文件路径问题...

标签: java sockets networking udp


【解决方案1】:

原因

MTU!

您正在通过 UDP 直接发送具有长缓冲区的数据包,这在大多数网络环境中可能无法正常工作。

通过 UDP 发送的数据包不应长于网络 MTU,否则会被丢弃。大多数网络节点(路由器/交换机/主机...)上的网络 MTU 可能不超过 1500,有时甚至更小。虽然有些节点可能会对 ip 数据包进行 sigmentation,但在使用 UDP 时不应该指望它。

建议

在这个应用程序中改用 TCP,至于:

  • 您正在发送预期完整的数据(否则它将无用)。

  • 你不关心拥塞控制算法。

所以就使用 TCP。

根据问题的更新进行编辑

所以,因为这是一个练习,你必须只使用 UDP。

因为一个文件可能没有用,除非它是完整的,你必须确保:

  • 所有数据包都可以通过该路径。这意味着网络应该物理连接和虚拟连接,并且数据包大小应该始终小于 MTU。
  • 如果有任何数据包丢失,接收方和发送方都应该能够知道。
  • 如果任何 apcket 出现故障,接收方应该能够知道。
  • 发送方应该能够缓存并重新发送尚未得到接收方确认的数据包。

确保您的网络连接良好。将图像缓冲区拆分为缓冲区数组,每个缓冲区项长度小于1000字节(应该是安全的)。

那么让我们为此设计一个成熟但简单的协议:

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | type                          | sequence number               |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
   | payload ...                                                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ...                                                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

对于类型,我们可能需要:

  • 你好:0x01
  • 再见:0x02
  • 确认:0x03
  • nack: 0x04
  • 数据:0x05
  • 反馈:0x06
  • ...

序列应该是单递增的。例如1, 2, 3, 4....(不必从1开始但OK)

它的工作原理如下:

Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)

# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i

Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)

# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1

Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)

# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2

Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)

# bye is not necessory

【讨论】:

  • 这是一个 UDP 练习。我知道 TCP 是正确的方法。我会添加一个关于它的注释。谢谢
  • @WhiteGlove 为 UDP 解决方案更新
【解决方案2】:

首先,我认为你需要学习如何在调试时使用wirshark或tcmpdump分析网络流,这将有助于你找出问题并解决它。

关于你的程序,user207421 已经提到了几个问题。我觉得还是用TCP比较好,但是如果你想通过这种方式学习UDP,你需要自己做一个苗条可靠的UDP。

例如,您可能需要以下型号

  • 建立发送缓冲区和接收缓冲区,每次检查缓冲区是否为空,如果没有,发送/接收并处理。(因为UDP有MTU)

  • 在每个数据报的头部添加一些额外的信息格式,包括整个消息的大小,数据报的顺序,左边的大小等。(因为你需要把你的消息切成很多部分)

  • 搭建控制器,需要有重传、重建消息等功能(由于UDP不可靠,需要检查各部分的完整性)

希望对你有帮助。

【讨论】:

  • 我今天将处理该头部信息。作业只需要基础知识,但我想给老师一个很好的应用程序。非常感谢您指出
  • 希望你能做好!而且我建议你先学习一些基础知识,比如udp socket模式,GBN和FEC这样的基本TCP设计思想也很好,但在TCP中没有使用。对于长期学习,您可能会发现 QUIC、libutp、KCP 或其他一些开源项目非常有帮助。
  • 还有,我给你的建议是如何正确实现你写的例子,如果你真的想使用UDP,你需要这样做..
最近更新 更多