UDP-клиент, написанный на Golang, не может получить сообщение от сервера

Я написал Java-клиент, который отправляет сообщение на широковещательный адрес.

Я также написал Java-сервер, который принимает все отправленные сообщения и отправляет сообщение обратно клиенту.

Теперь я хотел попробовать сделать то же самое в Go, просто для того, чтобы набраться опыта. Сервер работает нормально, получает сообщение и отвечает клиенту Java.

Но мой клиент Go только отправляет сообщение на сервер Go/Java, но не получает обратно никакого сообщения. Согласно wirehark, сообщение отправляется обратно на правильный IP-адрес и порт, но, по-видимому, порт недоступен.

Мой код выглядит следующим образом: Go Server:

package main

import (
    "fmt"
    "log"
    "net"
)

func main() {
    //Resolving address
    udpAddr, err := net.ResolveUDPAddr("udp4", "0.0.0.0:8888")

    if err != nil {
        log.Println("Error: ", err)
    }

    // Build listining connections
    conn, err := net.ListenUDP("udp", udpAddr)

    defer conn.Close()

    if err != nil {
        log.Println("Error: ", err)
    }

    // Interacting with one client at a time
    for {
        fmt.Println(">>>Ready to receive broadcast packets!")

        // Receiving a message
        recvBuff := make([]byte, 15000)
        _, rmAddr, err := conn.ReadFromUDP(recvBuff)

        if err != nil {
            panic(err)
        }

        fmt.Println(">>>Discovery packet received from: " + rmAddr.String())
        fmt.Println(">>>Packet received; data: " + string(recvBuff))

        // Sending the same message back to current client
        conn.WriteToUDP(recvBuff, rmAddr)

        fmt.Println(">>>Sent packet to: " + rmAddr.String())

} }

Перейти клиент:

package main

import (
    "fmt"
    "log"
    "net"
    "os"
)

func main() {
    service := "158.129.239.255:8888"

    // Resolving Address
    RemoteAddr, err := net.ResolveUDPAddr("udp", service)

    // Make a connection
    conn, err := net.DialUDP("udp", nil, RemoteAddr)

    defer conn.Close()

    // Exit if some error occured
    if err != nil {
        log.Fatal(err)
        os.Exit(1)
    }

    // write a message to server
    message := []byte("message")

    _, err = conn.Write(message)
    fmt.Println(">>> Request packet sent to: 158.129.239.255 (DEFAULT)")

    if err != nil {
        log.Println(err)
    }

    // Receive response from server
    buf := make([]byte, 15000)
    amountByte, remAddr, err := conn.ReadFromUDP(buf)

    if err != nil {
        log.Println(err)
    } else {
        fmt.Println(amountByte, "bytes received from", remAddr)
    }

}

Java-клиент:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BroadcastUDPClient {

    public static void main(String[] args) {
        // Find the server using UDP broadcast
        try {
            //Open a random port to send the package
            DatagramSocket sendSD = new DatagramSocket();
            sendSD.setBroadcast(true);

                byte[] sendData = "message".getBytes();

            //Try the 255.255.255.255 first
            try {
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("158.129.239.255"), 8888);
                sendSD.send(sendPacket);
                System.out.println(">>> Request packet sent to: 158.129.239.255 (DEFAULT)");
            } catch (Exception e) {
            }

            //Wait for a response
            byte[] recvBuf = new byte[15000];
            DatagramPacket receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
            sendSD.receive(receivePacket);

            //We have a response
            System.out.println(">>> Broadcast response from server: " + receivePacket.getAddress().getHostAddress());
            String message = new String(receivePacket.getData()).trim();
            System.out.println(">>> Message Body: " + message);

            //Close the port!
            sendSD.close();
        } catch (IOException ex) {
            Logger.getLogger(BroadcastUDPClient.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

Что я делаю не так с моим клиентом Go?


person Marvin Ahlgrimm    schedule 04.03.2016    source источник
comment
Не игнорируйте ошибку, которую вы получаете от WriteToUDP   -  person 0x434D53    schedule 04.03.2016
comment
Кроме того: вы не обрабатываете несколько других ошибок. И log.Fatal все равно завершает программу, os.Exit никогда не будет достигнут.   -  person 0x434D53    schedule 04.03.2016


Ответы (2)


Даже если вы собираетесь отправлять только UDP-пакеты из соединения, обычно следует использовать ListenUDP для создания подключения и используйте методы ReadFromUDP и WriteToUDP.

Когда вы используете DialUDP, он создает «подключенный» сокет UDP с неявной удаленной конечной точкой, которая будет фильтровать входящие пакеты. На справочной странице Linux connect:

Если сокет sockfd имеет тип SOCK_DGRAM, то адрес — это адрес, на который отправляются дейтаграммы по умолчанию, и единственный адрес, с которого дейтаграммы принимаются.

person JimB    schedule 04.03.2016
comment
Спасибо! Имеет смысл, что я буду получать сообщения только от подключенного соединения. Но опять же, почему Go создает соединение? Разве UDP не работает без подключения? - person Marvin Ahlgrimm; 04.03.2016
comment
@MarvinAhlgrimm: да и нет ;). Go показывает только то, что доступно из базового API сокетов Беркли. DialUDP использует системный вызов connect, поэтому вы можете отправлять и получать сообщения с одного хоста, который является частью этого стандартного API. UDP по-прежнему не требует установления соединения, но это обеспечивает удобство необходимости вызова только recv и send при общении с одним хостом и отсутствия необходимости отфильтровывать беспризорные пакеты. - person JimB; 04.03.2016
comment
Это была огромная помощь. Конечно, непонятно, почему ReadFrom не работает на DialUDP. Особенно, если учесть, что почти в каждом примере UDP используется Dial over Listen. - person Adam Lewis; 09.12.2016
comment
ДНС так работает? Если это правда, мне интересно, как DNS-сервер может отвечать отправителям, если они (отправители) находятся в частной сети. - person Ninh Pham; 25.07.2020

Если вы не проигнорируете ошибку, которую вы получите от WriteToUDP, она фактически выдаст вам ошибку: «sendto: message to long»

В OSX максимальный размер дейтаграммы UDP по умолчанию составляет 9216 байт. Вы пытаетесь отправить 15000 байт.

Если вы просто хотите написать ответ, что вы получили, напишите

recvBuff[:n]

, где n — количество байтов, полученных ранее.

person 0x434D53    schedule 04.03.2016
comment
Привет, спасибо, что изучили это. Я только что присвоил количество битов, ошибка из-за результата WriteToUDP. Для суммы битов установлено значение 7, а ошибка, по-видимому, равна нулю. Я также уменьшил количество байтов до разумного значения (256), но порт клиента Go по-прежнему недоступен согласно wireshark. - person Marvin Ahlgrimm; 04.03.2016
comment
У меня работает после. Какую машину/ОС и версию Go вы используете? Любой брандмауэр? Попробуйте слушать только на локальном хосте: 8888. - person 0x434D53; 04.03.2016
comment
Также: amountBytes не меняет длину вашего recvBuff. Также вы не должны называть его amountBytes, просто n. - person 0x434D53; 04.03.2016