Java 无法通过Android API 22(棒棒糖)上的套接字发送ICMP数据包

Java 无法通过Android API 22(棒棒糖)上的套接字发送ICMP数据包,java,android,sockets,kotlin,ping,Java,Android,Sockets,Kotlin,Ping,我编写了一个小应用程序,通过使用sockets(Android的操作系统包)发送ICMP数据包来ping远程服务器。我在API28模拟器上测试了它,它工作了,但在API22(Android 5.1棒棒糖)上不工作。我在VD和我的智能手机(同样的旧API)上测试了它,结果都是阴性的 android.system.ErrnoException: sendto failed: EINVAL (Invalid argument) 我在不同的API版本上调试了该应用程序,我注意到唯一的区别是ByteBu

我编写了一个小应用程序,通过使用sockets(Android的操作系统包)发送ICMP数据包来ping远程服务器。我在API28模拟器上测试了它,它工作了,但在API22(Android 5.1棒棒糖)上不工作。我在VD和我的智能手机(同样的旧API)上测试了它,结果都是阴性的

android.system.ErrnoException: sendto failed: EINVAL (Invalid argument)
我在不同的API版本上调试了该应用程序,我注意到唯一的区别是
ByteBuffer.wrap()
在API 22上生成
ByteArrayBuffer
实例,在较新版本上生成
HeapArrayBuffer
实例。有效载荷本身似乎是相同的

下面是代码示例(简化)。此外,还有一个可用的测试应用程序: 您可以尝试在不同的模拟器(API22和>22)上启动它,并查看差异

private val timeoutMs = 5000
private val delayMs = 500L
private val ECHO_PORT =  80
private val POLLIN = (if (OsConstants.POLLIN == 0) 1 else OsConstants.POLLIN).toShort()

fun ping(host: String): Unit {
    val inetAddress: InetAddress = InetAddress.getByName(host)
    if (inetAddress is Inet6Address) throw Exception("IPv6 implementation omitted for simplicity")
    val proto = OsConstants.IPPROTO_ICMP
    val inet = OsConstants.AF_INET
    val type = PacketBuilder.TYPE_ICMP_V4
    val socketFileDescriptor = Os.socket(inet, OsConstants.SOCK_DGRAM, proto)
    if (!socketFileDescriptor.valid()) throw Exception("Socket descriptor is invalid")    
    var sequenceNumber: Short = 0
    for (i in 0..2) {
        sequenceNumber++
        val echoPacketBuilder =
            PacketBuilder(type, "foobarbazquok".toByteArray())
                .withSequenceNumber(sequenceNumber)
        val buffer = echoPacketBuilder.build()

        /**
         * This is the command that throws an exception
         */
        val bytesSent = Os.sendto(socketFileDescriptor, buffer,0, buffer.size, 0, inetAddress, ECHO_PORT)

        // Response processing code omitted
    }
}

class PacketBuilder(val type: Byte, val payload: ByteArray, val sequenceNumber: Short = 0, val identifier: Short = 0xDBB) {
    private val MAX_PAYLOAD = 65507
    private val CODE: Byte = 0

    init {
        if (payload.size > MAX_PAYLOAD) throw Exception("Payload limited to $MAX_PAYLOAD")
    }

    fun build(): ByteArray {
        val buffer = ByteArray(8 + payload.size)
        val byteBuffer = ByteBuffer.wrap(buffer)

        byteBuffer.put(type)
        byteBuffer.put(CODE)
        val checkPos = byteBuffer.position()
        byteBuffer.position(checkPos + 2)
        byteBuffer.putShort(identifier)
        byteBuffer.putShort(sequenceNumber)
        byteBuffer.put(payload)
        byteBuffer.putShort(checkPos, checksum(buffer))
        byteBuffer.flip()
        return buffer
    }

    fun withSequenceNumber(sequenceNumber: Short): PacketBuilder {
        return PacketBuilder(type, payload, sequenceNumber, identifier)
    }

    /**
     * RFC 1071 checksum
     */
    private fun checksum(data: ByteArray): Short {
        var sum = 0
        // High bytes (even indices)
        for (i in 0 until data.size step 2) {
            sum += data[i].and(0xFF.toByte()).toInt() shl 8
            sum = (sum and 0xFFFF) + (sum shr 16)
        }

        // Low bytes (odd indices)
        for (i in 1 until data.size step 2) {
            sum += data[i] and 0xFF.toByte()
            sum = (sum and 0xFFFF) + (sum shr 16)
        }

        sum = (sum and 0xFFFF) + (sum shr 16)
        return (sum xor 0xFFFF).toShort()
    }

    companion object {
        val TYPE_ICMP_V4: Byte = 8
    }
}

如果我的代码不正确,那么我希望在所有平台上都会出现相同的错误,但正如我所说的,它确实适用于所有比22新的API,我不知道到底是什么导致了这个问题

21/22和23版本分别有不同的Linux内核版本3.16.1和3.18.10
Os
是低级API,所以问题可能会因为不同的Linux内核版本和要求而发生。也许您应该研究sendTo(2)Linux文档。但这只是一个猜测。