0

我想实现一个“简单”的 SSDP 发现客户端。意味着客户端应该发送一个

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 1
ST: ssdp:all

然后收听“网络”(?)以获取 IP 地址列表。

为了测试实现,我编写了一个单元测试,它创建了一个“假” MulticastServer,它只是听到 SSDP IP&Port,当收到一些东西时,发回相同的消息。

问题是这段代码大部分时间都在我的机器(macOS)上工作,但在我们的 CI 服务器(Linux)上却从来没有。我(macOS)有时会收到与 CI 相同的断言失败错误。但正如我所说 - 只是有时!不总是。我不知道为什么。

这是客户端的实现:

interface GatewayDiscoverer {

    companion object {
        val instance: GatewayDiscoverer = DefaultGatewayDiscoverer()
    }

    suspend fun discoverGateways(timeoutMillis: Int = 1000): List<String>
}

internal class DefaultGatewayDiscoverer : GatewayDiscoverer {

    override suspend fun discoverGateways(timeoutMillis: Int): List<String> {
        require(timeoutMillis in 1000..5000) {
            "timeoutMillis should be between 1000 (inclusive) and 5000 (inclusive)!"
        }

        val socket = DatagramSocket()
        sendSsdpPacket(socket)

        val gateways = receiveSsdpPacket(socket, timeoutMillis)

        return gateways
    }

    private fun sendSsdpPacket(socket: DatagramSocket) {
        val packetToSend =
            "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 1\r\nST: ssdp:all\r\n\r\n"
        val packetToSendAsBytes = packetToSend.toByteArray()
        val packet = DatagramPacket(
            packetToSendAsBytes,
            packetToSendAsBytes.size,
            InetAddress.getByName("239.255.255.250"),
            1900
        )
        socket.send(packet)
    }

    private fun receiveSsdpPacket(socket: DatagramSocket, timeoutInMillis: Int): List<String> {
        val gatewayList = mutableListOf<String>()

        while (true) {
            val receivedData = ByteArray(12)
            val packetToReceive = DatagramPacket(receivedData, receivedData.size)
            socket.soTimeout = timeoutInMillis
            try {
                socket.receive(packetToReceive)
                packetToReceive.address?.hostName?.let { gatewayList.add(it) }
            } catch (socketTimeout: SocketTimeoutException) {
                return gatewayList
            }
        }
    }
}

而这个测试(包括MulticastServer):

class DefaultGatewayDiscovererTest {

    @Test
    fun `discover gateways should return a list of gateway IPs`() = with(MulticastServer()) {
        start()

        val list = runBlocking { GatewayDiscoverer.instance.discoverGateways(1000) }

        close()
        assertThat(list.size).isEqualTo(1)
        assertThat(list).contains(InetAddress.getLocalHost().hostAddress)
        Unit
    }
}

/**
 * A "MulticastServer" which will join the
 * 239.255.255.250:1900 group to listen on SSDP events.
 * They will report back with the same package
 * it received.
 */
class MulticastServer : Thread(), Closeable {

    private val group = InetAddress.getByName("239.255.255.250")
    private val socket: MulticastSocket = MulticastSocket(1900)

    init {
        // This force to use IPv4...
        var netinterface: NetworkInterface? = null
        // Otherwise it will (at least on macOS) use IPv6 which leads to issues
        // while joining the group...
        val networkInterfaces = NetworkInterface.getNetworkInterfaces()
        while (networkInterfaces.hasMoreElements()) {
            val networkInterface = networkInterfaces.nextElement()
            val addressesFromNetworkInterface = networkInterface.inetAddresses
            while (addressesFromNetworkInterface.hasMoreElements()) {
                val inetAddress = addressesFromNetworkInterface.nextElement()
                if (inetAddress.isSiteLocalAddress
                    && !inetAddress.isAnyLocalAddress
                    && !inetAddress.isLinkLocalAddress
                    && !inetAddress.isLoopbackAddress
                    && !inetAddress.isMulticastAddress
                ) {
                    netinterface = NetworkInterface.getByName(networkInterface.name)
                }
            }
        }

        socket.joinGroup(InetSocketAddress("239.255.255.250", 1900), netinterface!!)
    }

    override fun run() {
        while (true) {
            val buf = ByteArray(256)
            val packet = DatagramPacket(buf, buf.size)
            try {
                socket.receive(packet)
            } catch (socketEx: SocketException) {
                break
            }
            // Print for debugging
            val message = String(packet.data, 0, packet.length)
            println(message)
            socket.send(packet)
        }
    }

    override fun close() = with(socket) {
        leaveGroup(group)
        close()
    }
}

当测试失败时,它在该行失败:

assertThat(list.size).isEqualTo(1)

list是空的。

经过一些调试,我发现MulticastServer没有收到消息。因此客户端没有收到响应并将 IP 地址添加到list.

我希望在没有那种“片状”的情况下,MulticastServer它将始终有效。我的实施有问题吗?

4

0 回答 0