Ktor network utilities - tvOS x64 target implementation of asynchronous TCP/UDP sockets and related networking functionality for the Ktor framework
—
Socket I/O operations using ByteChannel integration for reading and writing data with support for both stream-based and packet-based communication.
Combined interface for sockets that support both reading and writing operations.
/**
* Represents both readable and writable socket
*/
interface ReadWriteSocket : ASocket, AReadable, AWritableInterface and operations for reading data from sockets.
/**
* Represents a readable socket
*/
interface AReadable {
/**
* Attach channel for reading so incoming bytes appears in the attached channel.
* Only one channel could be attached
* @param channel ByteChannel to attach for reading
* @returns a job that does supply data
*/
fun attachForReading(channel: ByteChannel): WriterJob
}
/**
* Open a read channel, could be done only once
* @returns ByteReadChannel for reading data from the socket
*/
fun AReadable.openReadChannel(): ByteReadChannelUsage Example:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
val selectorManager = SelectorManager()
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
// Method 1: Open read channel directly
val readChannel = socket.openReadChannel()
val line = readChannel.readUTF8Line()
println("Received: $line")
// Method 2: Attach custom channel for reading
val customChannel = ByteChannel()
val readerJob = socket.attachForReading(customChannel)
// Read from custom channel
launch {
val data = customChannel.readUTF8Line()
println("Custom channel received: $data")
}
// Clean up
readerJob.cancel()
socket.close()Interface and operations for writing data to sockets.
/**
* Represents a writable socket
*/
interface AWritable {
/**
* Attach channel for writing so bytes written to the attached channel will be transmitted.
* Only one channel could be attached
* @param channel ByteChannel to attach for writing
* @returns a job that does transmit data from the channel
*/
fun attachForWriting(channel: ByteChannel): ReaderJob
}
/**
* Open a write channel, could be opened only once
* @param autoFlush whether returned channel do flush for every write operation
* @returns ByteWriteChannel for writing data to the socket
*/
fun AWritable.openWriteChannel(autoFlush: Boolean = false): ByteWriteChannelUsage Example:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
val selectorManager = SelectorManager()
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
// Method 1: Open write channel directly
val writeChannel = socket.openWriteChannel(autoFlush = true)
writeChannel.writeStringUtf8("Hello, Server!")
// autoFlush = true means data is sent immediately
// Method 2: Manual flush control
val manualWriteChannel = socket.openWriteChannel(autoFlush = false)
manualWriteChannel.writeStringUtf8("Message 1\n")
manualWriteChannel.writeStringUtf8("Message 2\n")
manualWriteChannel.flush() // Send both messages at once
// Method 3: Attach custom channel for writing
val customChannel = ByteChannel()
val writerJob = socket.attachForWriting(customChannel)
// Write to custom channel
launch {
customChannel.writeStringUtf8("Custom channel message")
customChannel.flush()
}
// Clean up
writerJob.cancel()
socket.close()Convenience class that combines a socket with its I/O channels for easier management.
/**
* Represents a connected socket with its input and output
*/
class Connection(
val socket: Socket,
val input: ByteReadChannel,
val output: ByteWriteChannel
)
/**
* Opens socket input and output channels and returns connection object
* @returns Connection object with socket and I/O channels
*/
fun Socket.connection(): ConnectionUsage Examples:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
import kotlinx.coroutines.*
val selectorManager = SelectorManager()
// Simple client using Connection
suspend fun simpleClient() {
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
val connection = socket.connection()
// Send request
connection.output.writeStringUtf8("GET /api/data HTTP/1.1\r\nHost: localhost\r\n\r\n")
connection.output.flush()
// Read response
val statusLine = connection.input.readUTF8Line()
println("Response: $statusLine")
// Read remaining headers
while (true) {
val header = connection.input.readUTF8Line()
if (header.isNullOrEmpty()) break
println("Header: $header")
}
connection.socket.close()
}
// Echo server using Connection
suspend fun echoServer() {
val serverSocket = aSocket(selectorManager).tcp().bind("localhost", 8080)
while (true) {
val clientSocket = serverSocket.accept()
launch {
val connection = clientSocket.connection()
try {
while (true) {
val line = connection.input.readUTF8Line() ?: break
connection.output.writeStringUtf8("Echo: $line\n")
connection.output.flush()
}
} finally {
connection.socket.close()
}
}
}
}
// Bidirectional communication
suspend fun chatClient() {
val socket = aSocket(selectorManager).tcp().connect("chat-server.com", 8080)
val connection = socket.connection()
// Send messages
launch {
while (true) {
val message = readLine() ?: break
connection.output.writeStringUtf8("$message\n")
connection.output.flush()
}
}
// Receive messages
launch {
while (true) {
val message = connection.input.readUTF8Line() ?: break
println("Received: $message")
}
}
// Keep connection alive
connection.socket.awaitClosed()
}Advanced patterns for handling complex I/O scenarios.
Binary Data Handling:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
suspend fun binaryDataExample() {
val selectorManager = SelectorManager()
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
val connection = socket.connection()
// Send binary data
connection.output.writeByte(0x01)
connection.output.writeInt(12345)
connection.output.writeFloat(3.14f)
connection.output.flush()
// Read binary data
val flag = connection.input.readByte()
val number = connection.input.readInt()
val pi = connection.input.readFloat()
println("Received: flag=$flag, number=$number, pi=$pi")
connection.socket.close()
selectorManager.close()
}Packet-based Communication:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
suspend fun packetBasedExample() {
val selectorManager = SelectorManager()
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
val connection = socket.connection()
// Send packet with length prefix
val message = "Hello, World!"
val messageBytes = message.toByteArray()
connection.output.writeInt(messageBytes.size) // Length prefix
connection.output.writeFully(messageBytes) // Message data
connection.output.flush()
// Read packet with length prefix
val length = connection.input.readInt()
val buffer = ByteArray(length)
connection.input.readFully(buffer)
val receivedMessage = buffer.decodeToString()
println("Received packet: $receivedMessage")
connection.socket.close()
selectorManager.close()
}Streaming Large Data:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
import kotlinx.coroutines.*
import java.io.File
suspend fun streamingExample() {
val selectorManager = SelectorManager()
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
val connection = socket.connection()
// Stream file upload
launch {
val file = File("large-file.dat")
val buffer = ByteArray(8192) // 8KB buffer
file.inputStream().use { fileInput ->
while (true) {
val bytesRead = fileInput.read(buffer)
if (bytesRead == -1) break
connection.output.writeFully(buffer, 0, bytesRead)
connection.output.flush()
}
}
connection.output.close() // Signal end of stream
}
// Stream file download
launch {
val outputFile = File("downloaded-file.dat")
val buffer = ByteArray(8192)
outputFile.outputStream().use { fileOutput ->
while (true) {
val bytesAvailable = connection.input.readAvailable(buffer)
if (bytesAvailable == -1) break
fileOutput.write(buffer, 0, bytesAvailable)
}
}
}
connection.socket.awaitClosed()
selectorManager.close()
}Error Handling:
import io.ktor.network.sockets.*
import io.ktor.network.selector.*
import io.ktor.utils.io.*
import kotlinx.coroutines.*
suspend fun errorHandlingExample() {
val selectorManager = SelectorManager()
try {
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080) {
socketTimeout = 10000 // 10 second timeout
}
val connection = socket.connection()
// Wrap I/O operations in try-catch
try {
withTimeout(5000) { // 5 second operation timeout
connection.output.writeStringUtf8("Hello")
connection.output.flush()
val response = connection.input.readUTF8Line()
println("Response: $response")
}
} catch (e: SocketTimeoutException) {
println("Socket timeout: ${e.message}")
} catch (e: TimeoutCancellationException) {
println("Operation timeout: ${e.message}")
} catch (e: Exception) {
println("I/O error: ${e.message}")
} finally {
connection.socket.close()
}
} catch (e: ConnectException) {
println("Connection failed: ${e.message}")
} finally {
selectorManager.close()
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-network-tvosx64