fixes, tcp/ip optimizations
This commit is contained in:
1 parent
bcf0140edb
commit
96edbb2040
6 files changed
+97
-50
No files matched your search
@@ -3,13 +3,14 @@ package net.sergeych.kiloparsec.adapter
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import kotlinx.coroutines.channels.ClosedSendChannelException
|
||||
import net.sergeych.bintools.toDump
|
||||
import net.sergeych.crypto.encodeVarUnsigned
|
||||
import net.sergeych.crypto.readVarUnsigned
|
||||
import net.sergeych.kiloparsec.Transport
|
||||
import net.sergeych.mp_logger.LogTag
|
||||
import net.sergeych.mp_logger.warning
|
||||
import net.sergeych.mp_tools.globalLaunch
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.StandardSocketOptions.TCP_NODELAY
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.AsynchronousSocketChannel
|
||||
@@ -18,52 +19,73 @@ import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
private val log = LogTag("ASTD")
|
||||
|
||||
/**
|
||||
* Prepend block with its size, varint-encoded
|
||||
*/
|
||||
private fun encode(block: UByteArray): ByteArray
|
||||
= (encodeVarUnsigned(block.size.toUInt()) + block).toByteArray()
|
||||
|
||||
/**
|
||||
* Convert asynchronous socket to a [Transport.Device] using non-blocking nio,
|
||||
* in a coroutine-effective manner. Note that it runs coroutines to read/write
|
||||
* to the socket in a global scope.These are closed when transport is closed
|
||||
* or the socket is closed, for example, by network failure.
|
||||
*/
|
||||
suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): Transport.Device {
|
||||
val deferredDevice = CompletableDeferred<Transport.Device>()
|
||||
suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): InetProxyDevice {
|
||||
val deferredDevice = CompletableDeferred<InetProxyDevice>()
|
||||
globalLaunch {
|
||||
coroutineScope {
|
||||
fun stop() {
|
||||
cancel()
|
||||
}
|
||||
socket.setOption(TCP_NODELAY, true)
|
||||
// socket input is to be parsed for blocks, so we receive bytes
|
||||
// and decode them to blocks
|
||||
val input = Channel<UByte>(1024)
|
||||
val output = Channel<UByte>(1024)
|
||||
// copy from socket to input
|
||||
// copy from socket to input:
|
||||
launch {
|
||||
val inb = ByteBuffer.allocate(1024)
|
||||
val data = ByteArray(1024)
|
||||
val inb = ByteBuffer.wrap(data)
|
||||
while (isActive) {
|
||||
inb.position(0)
|
||||
val size: Int = suspendCoroutine { continuation ->
|
||||
socket.read(inb, continuation, IntCompletionHandler)
|
||||
}
|
||||
if (size < 0) stop()
|
||||
else for (i in 0..<size) input.send(inb[i].toUByte().also { print(it.toInt().toChar()) })
|
||||
else {
|
||||
println("recvd:\n${data.sliceArray(0..<size).toDump()}\n------------------")
|
||||
for (i in 0..<size) input.send(data[i].toUByte())
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy from output tp socket
|
||||
// output is blocks, so we sent blocks:
|
||||
val outputBlocks = Channel<UByteArray>()
|
||||
// copy from output to socket:
|
||||
launch {
|
||||
val outBuff = ArrayList<Byte>(1024)
|
||||
try {
|
||||
while (isActive) {
|
||||
outBuff.clear()
|
||||
outBuff.add(output.receive().toByte())
|
||||
while (!output.isEmpty)
|
||||
outBuff.add(output.receive().toByte())
|
||||
suspendCoroutine { continuation ->
|
||||
socket.write(ByteBuffer.wrap(outBuff.toByteArray()), continuation, IntCompletionHandler)
|
||||
// wait for the first block to send
|
||||
var data = encode(outputBlocks.receive())
|
||||
// if there are more, take them all (NO_DELAY optimization)
|
||||
while (!outputBlocks.isEmpty)
|
||||
data += encode(outputBlocks.receive())
|
||||
// now send the aggregate:
|
||||
val outBuff = ByteBuffer.wrap(data)
|
||||
val cnt = suspendCoroutine { continuation ->
|
||||
socket.write(outBuff, continuation, IntCompletionHandler)
|
||||
}
|
||||
// be sure it was all sent
|
||||
if( outBuff.position() != data.size || cnt != data.size) {
|
||||
throw RuntimeException("PArtial write!")
|
||||
}
|
||||
}
|
||||
} catch (_: ClosedReceiveChannelException) {
|
||||
stop()
|
||||
}
|
||||
}
|
||||
// pump blocks from socket output to device input
|
||||
// transport device copes with blocks:
|
||||
val inputBlocks = Channel<UByteArray?>()
|
||||
// decode blocks from a byte channel read from the socket:
|
||||
launch {
|
||||
try {
|
||||
while (isActive) {
|
||||
@@ -84,20 +106,10 @@ suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): Transport.De
|
||||
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()
|
||||
}
|
||||
}
|
||||
// SocketAddress.
|
||||
val addr = socket.remoteAddress as InetSocketAddress
|
||||
deferredDevice.complete(
|
||||
ProxyDevice(inputBlocks, outputBlocks) { stop() }
|
||||
InetProxyDevice(inputBlocks, outputBlocks, JvmNetworkAddress(addr.address,addr.port)) { stop() }
|
||||
)
|
||||
}
|
||||
globalLaunch { socket.close() }
|
||||
|
||||
Reference in new issue
Block a user