我不太确定哪一方是导致我的Android应用程序无法从Golang服务器接收UDP“消息”的原因

问题描述 投票:0回答:1

我目前正在开发需要使用UDP进行通信的服务器和应用程序。我的服务器使用Golang,相关代码如下:

import (
    "flag"
    "fmt"
    "net"
    "os"
)

var (
    UDP_SOCKET_PORT  = flag.Int("udp_socket_server_port", 10001, "Socket sckServer port")
    udpSocketAddrArr = []net.Addr{}
    _udpConn         net.PacketConn
    _broadcastAddr   *net.UDPAddr
)

func StartUDPSocket() {
    _udpConn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", *UDP_SOCKET_PORT))
    if err != nil {
        panic(err)
    }

    defer _udpConn.Close()
    fmt.Printf("conn: %s\n", _udpConn.LocalAddr())

    _broadcastAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", getBroadcastIP(), *UDP_SOCKET_PORT))
    if err != nil {
        panic(err)
    }

    fmt.Printf("UDP serving at %s:%d...\n", _broadcastAddr.IP, _broadcastAddr.Port)

    for {
        buf := make([]byte, 2048)
        _, addr, err := _udpConn.ReadFrom(buf)
        if err != nil {
            panic(err)
        }

        udpSocketAddrArr = append(udpSocketAddrArr, addr)
        messageTxt := string(buf)

        checkUDPPayload(messageTxt, _udpConn, _broadcastAddr)
    }
}

func UDPSend(payload string) {
    for _, el := range udpSocketAddrArr {
        udpConnWrite(payload, GetUDPConn(), el)
    }
}

func UDPBroadcast(payload string) {
    udpConnWrite(payload, _udpConn, _broadcastAddr)
}

func udpConnWrite(payload string, conn net.PacketConn, addr net.Addr) {
    bytesWritten, err := conn.WriteTo([]byte(payload), addr)
    if err != nil {
        panic(err)
    }

    fmt.Printf("UDP Wrote %d bytes to %s\n", bytesWritten, addr.String())
}

func getBroadcastIP() string {
    return "255.255.255.255"
}

func GetCurrentIP() string {
    //find current ip
    mIpV4 := ""
    host, _ := os.Hostname()
    addrs, _ := net.LookupIP(host)
    for _, addr := range addrs {
        if ipv4 := addr.To4(); ipv4 != nil {
            mIpV4 = ipv4.String()
        }
    }

    return mIpV4
}

func GetUDPConn() net.PacketConn {
    return _udpConn
}

func GetUDPBroadcastConn() *net.UDPAddr {
    return _broadcastAddr   
}

然后在

checkUDPPayload()
里面,我有一个如下所示的代码:

func checkUDPPayload(payload string, conn net.PacketConn, addr net.Addr) {
    ...
    udpConnWrite(
        "some string",
        conn,
        addr,
    )
} 

它可以工作,它从我的Android应用程序接收有效负载(由于某种原因,让它在我的家庭网络上工作是一件痛苦的事情,并且在工作中工作相当容易)。但是我的应用程序无法接收服务器发送的数据包,并且我的应用程序可以接收它自己的数据包(我想这是有道理的)。我的 Android 代码如下所示:

private val broadcastAddr: InetAddress = InetAddress.getByName("255.255.255.255")
private lateinit var localBroadcastAddr: InetAddress
private lateinit var connectedToServerSocket: DatagramSocket
private lateinit var appSocket: DatagramSocket
val socketRespObservers = mutableListOf<(String) -> Unit>()
private var socketResp: String by Delegates.observable("") { _, _, newVal ->
    socketRespObservers.forEach { it(newVal) }
}
private lateinit var ctx: FragmentActivity

fun start(ctx: FragmentActivity) {
    [email protected] = ctx

    //socket communication
    ctx.lifecycleScope.launch(Dispatchers.IO) {
        try {
            connectedToServerSocket = DatagramSocket()
            appSocket = DatagramSocket(UDP_SERVER_PORT)

            connectedToServerSocket.connect(getBroadcastAddr(), UDP_SERVER_PORT)
            connectedToServerSocket.broadcast = true

            Log.i(MainActivity.TAG, "connectedToServerSocket connected: ${connectedToServerSocket.isConnected}")
            Log.i(MainActivity.TAG, "appSocket connected: ${appSocket.isConnected}")
            
            sendToUDP("some string", getLocalBroadcastAddr())

            while (true) {
                receiveFromUDP()
                receiveFromServerUDP()
            }
        } catch (e: SocketException) {
            Log.e(TAG, "Socket Error:", e)
            close()
        }catch (e: SecurityException) {
            Log.e(TAG, "Sec Error:", e)
            close()
        } catch (e: IOException) {
            Log.e(TAG, "IO Error:", e)
            close()
        } catch (e: Exception) {
            Log.e(TAG, "Error:", e)
            close()
        }
    }
}

fun sendToUDP(payload: String, srvAddr: InetAddress) {
    try {
        val sendPacket =
            DatagramPacket(
                payload.toByteArray(),
                payload.length,
                srvAddr,
                UDP_SERVER_PORT
            )
            
        appSocket.send(sendPacket)
    }catch (e : java.lang.Exception){
        Log.e(TAG, "err sending udp: ${e.localizedMessage}")
    }
}

private fun receiveFromUDP() {
    val receiveData = ByteArray(1024)
    val receivePacket = DatagramPacket(receiveData, receiveData.size)

    appSocket.receive(receivePacket)
    socketResp = String(receivePacket.data, 0, receivePacket.length)
}

private fun receiveFromServerUDP() {
    val receiveData = ByteArray(1024)
    val receivePacket = DatagramPacket(receiveData, receiveData.size)

    connectedToServerSocket.receive(receivePacket)
    socketResp = String(receivePacket.data, 0, receivePacket.length)
}

fun getBroadcastAddr() : InetAddress {
    return broadcastAddr
}

fun getLocalBroadcastAddr() : InetAddress {
    if(!::localBroadcastAddr.isInitialized){
        val currentIp = Utils.instance.getIpv4HostAddress()
        Log.i(TAG, "currentIp: $currentIp")
        val currentIpArr = currentIp.split(".")
        localBroadcastAddr = InetAddress.getByName("${currentIpArr[0]}.${currentIpArr[1]}.${currentIpArr[2]}.255")
    }

    return localBroadcastAddr
}

fun close() {
    appSocket.close()
}

companion object {
    const val TAG = "SocketHelper"
    val instance: SocketHelper by lazy {
        SocketHelper()
    }
    const val UDP_SERVER_PORT = 10001
}

工作中运行的代码使用

255.255.255.255
进行发送,而
DatagramSocket
不需要端口。我使用
DatagramSocket
来发送和接收。正如你所看到的,代码更丑陋,因为我尝试有 2 个
DatagramSocket
来查看是否有不同的配置
DatagramSocket
,我可能会收到服务器对我的“消息”的响应,就像现在,在我的家庭网络中,应用程序无法接收服务器的数据包,只能接收应用程序本身发送的数据包。
connectedToServerSocket.isConnected
返回true,但我看到一个答案,它是为
TCP
准备的,即使它说它已连接,它似乎对我没有任何用处。

更新:

我今天做了一些测试并继续做了一些研究,但我仍然无法完成这项工作。希望当我回到工作岗位时不会有任何问题。我对双方都做了一些小改动。现在我在想,也许这个问题是由我家里的本地网络引起的。我的 ISP 有点蹩脚,就像目前我的 NAT 设置为严格,我认为这是由于我没有固定 IP 造成的(因为它的成本几乎与我当前的预付费互联网计划一样多),所以我怀疑我的调制解调器的配置可能会影响我本地网络上的 UDP 通信。所以我买了一个路由器,认为如果我将我的电脑和手机连接到它的网络,我至少会有更多的控制权,至少允许 UDP 端口,这样我的系统就可以工作,但这似乎并不那么容易。我已经激活了 UPnP,但我的 golang 服务器似乎没有包含在允许或可以使用 UPnP 的应用程序中。

更新:

刚下班回家,我的更改确实破坏了系统在我工作网络上的工作。然后我检查了我以前的工作代码,至少在android方面,它是最简单的。它看起来像这样:

private lateinit var basicSocket: DatagramSocket
private val broadcastAddr: InetAddress = InetAddress.getByName("255.255.255.255")
basicSocket = DatagramSocket()

basicSendToUDP("some string", getBroadcastAddr())

while (true) {
    basicReceiveFromUDP()
}

fun basicSendToUDP(payload: String, iAddr: InetAddress) {
    Thread {
        val sendPacket =
            DatagramPacket(
                payload.toByteArray(),
                payload.length,
                iAddr,
                UDP_SERVER_PORT
            )
        basicSocket.send(sendPacket)
    }.start()
}

private fun basicReceiveFromUDP() {
    val receiveData = ByteArray(1024)
    val receivePacket = DatagramPacket(receiveData, receiveData.size)

    basicSocket.receive(receivePacket)
    socketResp = String(receivePacket.data, 0, receivePacket.length)
}

fun getBroadcastAddr(): InetAddress {
    return broadcastAddr
}

然后这是 golang 方面的样子,它与我之前发布的没有什么不同:

import (
    "flag"
    "fmt"
    "net"
    "os"
)

var (
    UDP_SOCKET_PORT  = flag.Int("udp_socket_server_port", 10001, "Socket sckServer port")
    udpSocketAddrArr = []net.Addr{}
    _udpServer         net.PacketConn
    _broadcastAddr   *net.UDPAddr
)

func StartUDPSocket() {
    conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", *UDP_SOCKET_PORT))
    if err != nil {
        panic(err)
    }

    _udpServer = conn//don't remove this; this seems to be quite important
    defer _udpServer.Close()

    fmt.Printf("UDP serving at %v...\n", _udpServer.LocalAddr().String())

    _broadcastAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", getBroadcastIP(), *UDP_SOCKET_PORT))
    if err != nil {
        panic(err)
    }

    for {
        buf := make([]byte, 2048)
        _, addr, err := _udpServer.ReadFrom(buf)
        if err != nil {
            panic(err)
        }

        udpSocketAddrArr = append(udpSocketAddrArr, addr)
        messageTxt := string(buf)

        checkUDPPayload(messageTxt, _udpServer, addr)
    }
}

func UDPSend(payload string) {
    for _, el := range udpSocketAddrArr {
        udpConnWrite(payload, GetUDPConn(), el)
    }
}

func UDPBroadcast(payload string) {
    udpConnWrite(payload, GetUDPConn(), GetUDPBroadcastConn())
}

func udpConnWrite(payload string, conn net.PacketConn, addr net.Addr) {
    if conn == nil {
        fmt.Println("udpConnWrite conn nil")
    }

    if addr == nil {
        fmt.Println("udpConnWrite addr nil")
    }

    bytesWritten, err := conn.WriteTo([]byte(payload), addr)
    if err != nil {
        panic(err)
    }

    fmt.Printf("UDP Wrote %d bytes to %s\n", bytesWritten, addr.String())
}

func getBroadcastIP() string {
    return "255.255.255.255"
}

func GetCurrentIP() string {
    //find current ip
    mIpV4 := ""
    host, _ := os.Hostname()
    addrs, _ := net.LookupIP(host)
    for _, addr := range addrs {
        if ipv4 := addr.To4(); ipv4 != nil {
            mIpV4 = ipv4.String()
        }
    }

    return mIpV4
}

func GetUDPConn() net.PacketConn {
    return _udpServer
}

func GetUDPBroadcastConn() *net.UDPAddr {
    return _broadcastAddr   
}

func GetUDPConnLst() []net.Addr {
    return udpSocketAddrArr
}

func ClearUDPConnLst() {
    udpSocketAddrArr = nil
}

那么,当最简单的代码在我的工作网络上完美运行时,我的家庭网络可能会出现什么问题?

android go udp router datagram
1个回答
0
投票

在 Golang 服务器代码中,您使用相同的端口来侦听传入消息和广播(由 UDP 等无连接协议支持)。

// Check if UDP Server is listening at the correct address and port
func StartUDPSocket() {
    // Consider printing this information to confirm it is what you expect.
    fmt.Printf("Listening on UDP port: %d\n", *UDP_SOCKET_PORT)
    // ...
}

确保端口在您的本地网络中已打开且可访问。您还可以检查数据包连接是否正确设置。

另外,在更新的 Go 代码中,缓冲区

buf
的大小被硬编码为 2048 字节。如果传入数据包较大,导致截断,或者如果传入数据包较小,则可能导致不必要的内存分配。

并且您当前的代码使用

panic(err)
来处理错误,这不是生产代码中的最佳方法,因为它会导致整个应用程序崩溃。

for {
    buf := make([]byte, 2048)
    _, addr, err := _udpServer.ReadFrom(buf)
    if err != nil {
        panic(err)
    }
    // other logic
}

您可以考虑,例如:

const maxUDPSize = 65507 // The maximum UDP packet size for IPv4

func StartUDPSocket() {
    conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", *UDP_SOCKET_PORT))
    if err != nil {
        log.Fatalf("Could not start UDP server: %v", err) // Replaced panic with log
        return
    }

    _udpServer = conn
    defer _udpServer.Close()

    // (other code)

    for {
        buf := make([]byte, maxUDPSize) // Allocate buffer with maximum UDP size
        n, addr, err := _udpServer.ReadFrom(buf)
        if err != nil {
            log.Printf("Error reading UDP packet: %v", err) // Logging instead of panicking
            continue // Skip to next iteration instead of stopping the entire application
        }

        udpSocketAddrArr = append(udpSocketAddrArr, addr)
        messageTxt := string(buf[:n]) // Only consider received bytes

        checkUDPPayload(messageTxt, _udpServer, addr)
    }
}

我将 2048 字节的硬编码缓冲区长度更改为 65507 字节,这是 IPv4 网络中 UDP 数据包的最大大小。这可确保您不会错过传入数据包的任何部分。此外,我将缓冲区切片为

buf[:n]
以仅包含实际接收到的字节。

我还将

panic(err)
替换为
log.Printf()
以记录错误。这样,如果发生错误,应用程序就不会崩溃。我添加了一个
continue
语句,以便在出现错误时跳到下一次迭代。


在您的 Android 代码中,您正在将数据发送到

255.255.255.255
。这是一个“有限广播地址”,它将数据包广播到本地网络中的所有主机。但是,这可能并不总是有效,具体取决于您的网络配置。 // Using 255.255.255.255 may not always work. // You might want to use the exact server's IP address to debug. private val broadcastAddr: InetAddress = InetAddress.getByName("255.255.255.255");

我会在函数中添加一些调试信息
receiveFromServerUDP

:

private fun receiveFromServerUDP() {
    val receiveData = ByteArray(1024);
    val receivePacket = new DatagramPacket(receiveData, receiveData.length);

    // Log a message before trying to receive to confirm that the code reaches this point
    Log.i(TAG, "Waiting to receive UDP packet");
    connectedToServerSocket.receive(receivePacket);
    
    // Log received data
    socketResp = new String(receivePacket.getData(), 0, receivePacket.getLength());
    Log.i(TAG, "Received UDP packet: " + socketResp);
}

检查您的本地防火墙设置是否可能
阻止传入或传出 UDP 数据包

。既然您提到您的家庭网络有严格的 NAT,这可能是导致您问题的原因之一。 您提到您的 ISP 不提供固定 IP,并且您怀疑调制解调器的配置可能会影响 UDP 通信。检查您的调制解调器或路由器是否有可能影响 UDP 流量的设置。此外,UPnP 设置通常与本地网络通信无关,因此启用或禁用它不会对您的具体情况产生影响(您尝试调试的代码,其中涉及 Golang 服务器和 Android 客户端之间的直接 UDP 通信) ).

© www.soinside.com 2019 - 2024. All rights reserved.