Java 无法通过Android API 22(棒棒糖)上的套接字发送ICMP数据包
我编写了一个小应用程序,通过使用sockets(Android的操作系统包)发送ICMP数据包来ping远程服务器。我在API28模拟器上测试了它,它工作了,但在API22(Android 5.1棒棒糖)上不工作。我在VD和我的智能手机(同样的旧API)上测试了它,结果都是阴性的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
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文档。但这只是一个猜测。