async socket closing logic

This commit is contained in:
sergeych committed 2023-11-14 02:50:42 +03:00
1 parent 1814572a04
commit 75e91f8092
3 files changed
+93 -73

No files matched your search

@@ -24,15 +24,17 @@ actual fun acceptTcpDevice(port: Int): Flow<Transport.Device> {
it.bind(InetSocketAddress(InetAddress.getLocalHost(), port))
}
}
println("Server socket ready $socket")
val connectedSocket = suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
socket.close()
while (true) {
println("Server socket ready $socket")
val connectedSocket = suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
socket.close()
}
socket.accept(continuation, ContinuationHandler())
}
socket.accept(continuation, ContinuationHandler())
println("incoming connection")
emit(asyncSocketToDevice(connectedSocket))
}
println("incoming connection")
emit(asyncSocketToDevice(connectedSocket))
}
}
@@ -1,12 +1,9 @@
package net.sergeych.kiloparsec.adapter
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.cancel
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.channels.ClosedSendChannelException
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import net.sergeych.crypto.encodeVarUnsigned
import net.sergeych.crypto.readVarUnsigned
import net.sergeych.crypto.toDump
@@ -28,78 +25,81 @@ suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): Transport.De
globalLaunch {
fun stop() {
cancel()
runCatching { socket.close() }
}
val input = Channel<UByte>(1024)
val output = Channel<UByte>(1024)
// copy from socket to input
launch {
val inb = ByteBuffer.allocate(1024)
while (isActive) {
val size: Int = suspendCoroutine { continuation ->
socket.read(inb, continuation, IntCompletionHandler)
}
println("--------- read chunk $size")
if (size < 0) stop()
else for (i in 0..<size) input.send(inb[i].toUByte().also { print(it.toInt().toChar())})
}
}
// copy from output tp socket
launch {
val outBuff = ArrayList<Byte>(1024)
try {
coroutineScope {
launch {
val inb = ByteBuffer.allocate(1024)
while (isActive) {
outBuff.clear()
outBuff.add(output.receive().toByte())
while (!output.isEmpty)
val size: Int = suspendCoroutine { continuation ->
socket.read(inb, continuation, IntCompletionHandler)
}
println("--------- read chunk $size")
if (size < 0) stop()
else for (i in 0..<size) input.send(inb[i].toUByte().also { print(it.toInt().toChar()) })
}
}
// copy from output tp socket
launch {
val outBuff = ArrayList<Byte>(1024)
try {
while (isActive) {
outBuff.clear()
outBuff.add(output.receive().toByte())
suspendCoroutine { continuation ->
socket.write(ByteBuffer.wrap(outBuff.toByteArray()), continuation, IntCompletionHandler)
}
}
} catch (_: ClosedReceiveChannelException) {
stop()
}
}
// pump blocks from socket output to device input
val inputBlocks = Channel<UByteArray?>()
launch {
try {
while (isActive) {
val size = readVarUnsigned(input)
println("expected size $size")
if (size == 0u) println("*** zero size block is ignored!")
else {
val block = UByteArray(size.toInt())
for (i in 0..<size.toInt()) {
block[i] = input.receive()
while (!output.isEmpty)
outBuff.add(output.receive().toByte())
suspendCoroutine { continuation ->
socket.write(ByteBuffer.wrap(outBuff.toByteArray()), continuation, IntCompletionHandler)
}
println("ready block:\n${block.toDump()}")
inputBlocks.send(block)
}
} catch (_: ClosedReceiveChannelException) {
stop()
}
} catch (_: CancellationException) {
inputBlocks.send(null)
} catch (_: ClosedReceiveChannelException) {
inputBlocks.send(null)
stop()
}
}
val outputBlocks = Channel<UByteArray>()
launch {
try {
while (isActive) {
val block = outputBlocks.receive()
output.sendAll(encodeVarUnsigned(block.size.toUInt()))
output.sendAll(block)
// pump blocks from socket output to device input
val inputBlocks = Channel<UByteArray?>()
launch {
try {
while (isActive) {
val size = readVarUnsigned(input)
println("expected size $size")
if (size == 0u) println("*** zero size block is ignored!")
else {
val block = UByteArray(size.toInt())
for (i in 0..<size.toInt()) {
block[i] = input.receive()
}
println("ready block:\n${block.toDump()}")
inputBlocks.send(block)
}
}
} catch (_: CancellationException) {
inputBlocks.send(null)
} catch (_: ClosedReceiveChannelException) {
inputBlocks.send(null)
stop()
}
} catch (_: ClosedSendChannelException) {
stop()
}
val outputBlocks = Channel<UByteArray>()
launch {
try {
while (isActive) {
val block = outputBlocks.receive()
output.sendAll(encodeVarUnsigned(block.size.toUInt()))
output.sendAll(block)
}
} catch (_: ClosedSendChannelException) {
stop()
}
}
deferredDevice.complete(
ProxyDevice(inputBlocks, outputBlocks) { stop() }
)
}
deferredDevice.complete(
ProxyDevice(inputBlocks, outputBlocks) { stop() }
)
globalLaunch { socket.close() }
}
return deferredDevice.await()
}