https://kotlinlang.org logo
#ktor
Title
# ktor
a

Andrey Gromov

05/30/2019, 3:26 PM
Hi! I trying to work with UDP sockets and have some problem. Every time I start my code, I have 50/50 chance to start normally or receive java.net.SocketException: Already bound
Copy code
private suspend fun eventLoop(port: Int) {
        val server = aSocket(ActorSelectorManager(<http://Dispatchers.IO|Dispatchers.IO>))
            .udp()
            .bind(InetSocketAddress(port))       <<<<<<<<<<< Exception
I'm sure that port is free on bind moment Stacktrace
Copy code
Exception in thread "main" java.net.SocketException: Already bound
	at java.base/sun.nio.ch.Net.translateToSocketException(Net.java:164)
	at java.base/sun.nio.ch.DatagramSocketAdaptor.bind(DatagramSocketAdaptor.java:105)
	at io.ktor.network.sockets.UDPSocketBuilder.bind(Builders.kt:152)
	at io.ktor.network.sockets.UDPSocketBuilder.bind$default(Builders.kt:143)
	at co.fun.multiplayer.udp.ServerListener.eventLoop(ServerListener.kt:34)
	at co.fun.multiplayer.udp.ServerListener$start$1.invokeSuspend(ServerListener.kt:26)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:80)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at co.fun.multiplayer.udp.ServerListener.start(ServerListener.kt:25)
	at co.fun.multiplayer.udp.MainKt.main(Main.kt:14)
Caused by: java.nio.channels.AlreadyBoundException
	at java.base/sun.nio.ch.DatagramChannelImpl.bind(DatagramChannelImpl.java:784)
	at java.base/sun.nio.ch.DatagramSocketAdaptor.bind(DatagramSocketAdaptor.java:103)
	... 14 more
This workaround helping, but it is very ugly
Copy code
var server: BoundDatagramSocket
        while (true) {
            try {
                server = aSocket(ActorSelectorManager(<http://Dispatchers.IO|Dispatchers.IO>))
                    .udp()
                    .bind(InetSocketAddress(port))
                break
            } catch (e: Exception) {

            }
        }
c

cy

05/30/2019, 3:34 PM
Most likely it is because UDP socket doesn't free for a while after application exit. Not sure if we can do anything with it
a

Andrey Gromov

05/30/2019, 3:51 PM
@cy not "a while". Problem reproduced whether one second or ten minutes between restarts. Also, trick with while+try is really work. And work fast - one, two iterations before port normally bind.
Also, I do not have any problem of that kind with absolutely similar UDP-server written on .NET Core
d

Dico

05/30/2019, 4:10 PM
Next time can you please post the stack trace as a reply to your own thread so it doesn't fill up 2 phone screens
c

cy

05/30/2019, 4:12 PM
I see no reason for "already bound": datagram socket is bound only once just after it's creation
Is it windows?
a

Andrey Gromov

05/30/2019, 4:30 PM
@cy Darwin Kernel Version 18.6.0
c

cy

05/30/2019, 4:31 PM
I can't reproduce, neither Linux nor Darwin
Are you sure you don't have it started twice for some reason?
a

Andrey Gromov

05/30/2019, 4:35 PM
@cy Yes. It is first that I checked. Also
netstat -a
does not contains used port
Maybe it important. I'm run it from Idea
Just now I added several new ports that not used before.
Copy code
runBlocking {
            arrayOf(5002, 5010, 5011, 5012).forEach { launch { eventLoop(it) } }
        }

    private suspend fun eventLoop(port: Int) {
        var server: BoundDatagramSocket
        while (true) {
            try {
                server = aSocket(ActorSelectorManager(<http://Dispatchers.IO|Dispatchers.IO>))
                    .udp()
                    .bind(InetSocketAddress(port))
                break
            } catch (e: Exception) {
                println("Oops!")
            }
        }

        println("Each! $port")
.....
Output:
Copy code
Oops!
Each! 5002   <<< This is original port
Exception in thread "DefaultDispatcher-worker-3" java.nio.channels.ClosedChannelException
	at java.base/java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:206)
	at io.ktor.network.selector.SelectorManagerSupport.applyInterest(SelectorManagerSupport.kt:116)
	at io.ktor.network.selector.ActorSelectorManager.process(ActorSelectorManager.kt:87)
	at io.ktor.network.selector.ActorSelectorManager$process$1.invokeSuspend(ActorSelectorManager.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at io.ktor.network.selector.ActorSelectorManager$ContinuationHolder.resume(ActorSelectorManager.kt:187)
	at io.ktor.network.selector.ActorSelectorManager.publishInterest(ActorSelectorManager.kt:141)
	at io.ktor.network.selector.SelectorManagerSupport.select(SelectorManagerSupport.kt:46)
	at io.ktor.network.sockets.DatagramSocketImpl.receiveSuspend(DatagramSocketImpl.kt:70)
	at io.ktor.network.sockets.DatagramSocketImpl.receiveImpl(DatagramSocketImpl.kt:59)
	at io.ktor.network.sockets.DatagramSocketImpl$receiver$1.invokeSuspend(DatagramSocketImpl.kt:36)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Oops!
Each! 5010
Oops!
Oops!
Oops!
Each! 5011
Each! 5012
c

cy

05/30/2019, 4:49 PM
println("Oops $port ${Thread.currentThread().name}!")
a

Andrey Gromov

05/31/2019, 6:13 AM
@cy
Copy code
> Task :MainKt.main()
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Oops! 5002 main!
Each! 5002 main!
Each! 5010 main!
Oops! 5011 main!
Each! 5011 main!
Oops! 5012 main!
Oops! 5012 main!
Oops! 5012 main!
Oops! 5012 main!
Oops! 5012 main!
Each! 5012 main!
It's very interesting. If FIRST binding established with
launch(<http://Dispatchers.IO|Dispatchers.IO>)
- exceptions frequency dramatically reduced, but do not removed at all.
Copy code
launch(<http://Dispatcher.IO|Dispatcher.IO>){eventLoop(5002)}
arrayOf(5010,5011,5012).forEach{
    launch{eventLoop(it)}
}
Note that I use IO only for first binding call.
Copy code
Each! 5002 DefaultDispatcher-worker-2!
Oops! 5010 main!
Each! 5010 main!
Each! 5011 main!
Each! 5012 main!
If I use
IO
for all bindings - problem does not appears.
Looks like
aSocket(ActorSelectorManager(<http://Dispatchers.IO|Dispatchers.IO>))
does not affects
c

cy

05/31/2019, 9:25 AM
Can you put a breakpoint exact at bind inside of socket impl at io.ktor.network.sockets.UDPSocketBuilder.bind(Builders.kt:152) ? Stop at breakpoint, check with netstat -l and then step over ?
a

Andrey Gromov

05/31/2019, 9:49 AM
netstat shows nothing. Execution jumps to
Copy code
inline fun <C : Closeable, R> SelectorManager.buildOrClose(create: SelectorProvider.() -> C, setup: C.() -> R): R {
    while (true) {
        val result = create(provider)

        try {
            return setup(result)
        } catch (t: Throwable) {
            result.close()       <<<<<<<<<<<<<<<
            throw t
        }
    }
}
@cy Exception trows from
Copy code
@Override
    public DatagramChannel bind(SocketAddress local) throws IOException {
        readLock.lock();
        try {
            writeLock.lock();
            try {
                synchronized (stateLock) {
                    ensureOpen();
                    if (localAddress != null)
                        throw new AlreadyBoundException();
                    bindInternal(local);
                }
            } finally {
                writeLock.unlock();
            }
        } finally {
            readLock.unlock();
        }
        return this;
    }
localAddress = /0:0:0:0:0:0:0:0:62107
@cy any progress?
187 Views