I'm trying to build a performant tcp proxy to bi-directionally forward connections from client A to server B via a proxy server C.
The connection would look like so:
A <——> C <——> B
My initial plan was to use KTor raw TCP Sockets, but the initial example on the website was pretty lack luster and didn’t really point me in the direction of accomplishing my task.
Some things I wanted to be able to do:
- register callbacks for when the client is closed
- register callbacks for when the server is closed
- clean up resources when either the client or server close
- point the data from one tcp buffer to another without copying it over
currently I have a very naive implementation as follows:
fun main() {
runBlocking {
launch(Dispatchers.IO) {
val proxySelectorManager = SelectorManager(Dispatchers.IO)
val proxyServer = aSocket(proxySelectorManager).tcp().bind(HOST, PORT)
println("Listening on: ${HOST}:${PORT}")
while (true) {
val clientSocket = proxyServer.accept()
val serverSelectorManager = SelectorManager(Dispatchers.IO)
val serverSocket = aSocket(serverSelectorManager).tcp().connect(REMOTE_HOST, REMOTE_PORT)
println("Client joined at: ${clientSocket.remoteAddress} -> ${clientSocket.localAddress}")
println("Connected to Server at: ${serverSocket.localAddress} -> ${serverSocket.remoteAddress}")
launch(Dispatchers.IO) {
val clientReadChannel = clientSocket.openReadChannel()
val clientWriteChannel = clientSocket.openWriteChannel(true)
val serverReadChannel = serverSocket.openReadChannel()
val serverWriteChannel = serverSocket.openWriteChannel(true)
launch(Dispatchers.IO) {
while (true) {
try { serverWriteChannel.writeByte(clientReadChannel.readByte()) }
catch (e: Exception) { break }
}
}
launch(Dispatchers.IO) {
while (true) {
try { clientWriteChannel.writeByte(serverReadChannel.readByte()) }
catch (e: Exception) { break }
}
}
}
}
}
}
}
My main issues currently are that I have no way of really knowing when a connection was closed (either the server or the client) and my current method of copying over the buffers is extremely slow and has lots of overhead.
Any thoughts or ideas on how I can accomplish the following are greatly appreciated.