Я пытался реализовать UDP Hole Punching в Kotlin / JVM, но он не работает так, как ожидалось. Отображения создаются в таблице NAT, и пакеты отправляются правильно, но мой NAT по-прежнему отклоняет их.
val peer1 = Peer(InetSocketAddress(5555)) // random port
val global1 = peer1.requestSTUN()
val peer2 = Peer(InetSocketAddress(6666)) // random port
val global2 = peer2.requestSTUN()
val bytes = "HELLO WORLD".toByteArray()
peer1.socket.close() // close old socket used for creating new one
peer2.socket.close() //
peer1.local = InetSocketAddress(5555) // create new sockets
peer1.socket = peer1.prepareSocket() //
peer2.local = InetSocketAddress(6666) //
peer2.socket = peer2.prepareSocket() //
println("CONNECT ${peer2.socket.localSocketAddress}$global2 to $global1")
peer2.socket.send(DatagramPacket(ByteArray(0), 0, global1))
println("PUNCH ${peer2.socket.localSocketAddress}$global2 to $global1")
println("CONNECT ${peer1.socket.localSocketAddress}$global1 to $global2")
peer1.socket.send(DatagramPacket(bytes, bytes.size, global2))
println("PUNCH ${peer1.socket.localSocketAddress}$global1 to $global2")
Thread { // ignored {} is just a function that cathes and outputs exception
println(peer1.requestSTUN())
repeat(500) {
peer1.socket.send(DatagramPacket(bytes, bytes.size, global2))
Thread.sleep(50)
}
}.start()
Thread {
println(peer2.requestSTUN())
repeat(500) {
peer2.socket.send(DatagramPacket(bytes, bytes.size, global1))
Thread.sleep(50)
}
}.start()
Thread {
repeat(50) {
ignored {
val receive = DatagramPacket(bytes, bytes.size)
peer1.socket.receive(receive)
println(String(receive.data))
}
}
}.start()
Thread {
repeat(50) {
ignored {
val receive = DatagramPacket(bytes, bytes.size)
peer2.socket.receive(receive)
println(String(receive.data))
}
}
}.start()
Вывод:
CONNECT 0.0.0.0/0.0.0.0:5555/178.162.116.157:5555 to /178.162.116.157:6666
PUNCH 0.0.0.0/0.0.0.0:5555/178.162.116.157:5555 to /178.162.116.157:6666
CONNECT 0.0.0.0/0.0.0.0:6666/178.162.116.157:6666 to /178.162.116.157:5555
PUNCH 0.0.0.0/0.0.0.0:6666/178.162.116.157:6666 to /178.162.116.157:5555
И затем он генерирует исключение SocketTimeoutException. Мой NAT является не симметричным c, потому что запросы к разным серверам STUN из одного сокета возвращают одинаковые внешние IP-адреса. Я проверил журнал Wireshark и похоже, что пакеты отклонены и сообщение ICMP Port Unavailable отправлено обратно. Если я использую один и тот же сокет для запроса к серверу STUN и для запроса к месту назначения, ничего не изменится.