MutlicastSocket并非总是接收

时间:2019-04-30 06:16:53

标签: networking kotlin multicastsocket ssdp

我想实现一个“简单的” 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在没有这种“松软”的情况下始终可以正常工作。我的实现有问题吗?

0 个答案:

没有答案