0
# I/O Operations
1
2
Socket I/O operations using ByteChannel integration for reading and writing data with support for both stream-based and packet-based communication.
3
4
## Capabilities
5
6
### Read/Write Socket Interface
7
8
Combined interface for sockets that support both reading and writing operations.
9
10
```kotlin { .api }
11
/**
12
* Represents both readable and writable socket
13
*/
14
interface ReadWriteSocket : ASocket, AReadable, AWritable
15
```
16
17
### Readable Socket Operations
18
19
Interface and operations for reading data from sockets.
20
21
```kotlin { .api }
22
/**
23
* Represents a readable socket
24
*/
25
interface AReadable {
26
/**
27
* Attach channel for reading so incoming bytes appears in the attached channel.
28
* Only one channel could be attached
29
* @param channel ByteChannel to attach for reading
30
* @returns a job that does supply data
31
*/
32
fun attachForReading(channel: ByteChannel): WriterJob
33
}
34
35
/**
36
* Open a read channel, could be done only once
37
* @returns ByteReadChannel for reading data from the socket
38
*/
39
fun AReadable.openReadChannel(): ByteReadChannel
40
```
41
42
**Usage Example:**
43
44
```kotlin
45
import io.ktor.network.sockets.*
46
import io.ktor.network.selector.*
47
import io.ktor.utils.io.*
48
49
val selectorManager = SelectorManager()
50
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
51
52
// Method 1: Open read channel directly
53
val readChannel = socket.openReadChannel()
54
val line = readChannel.readUTF8Line()
55
println("Received: $line")
56
57
// Method 2: Attach custom channel for reading
58
val customChannel = ByteChannel()
59
val readerJob = socket.attachForReading(customChannel)
60
61
// Read from custom channel
62
launch {
63
val data = customChannel.readUTF8Line()
64
println("Custom channel received: $data")
65
}
66
67
// Clean up
68
readerJob.cancel()
69
socket.close()
70
```
71
72
### Writable Socket Operations
73
74
Interface and operations for writing data to sockets.
75
76
```kotlin { .api }
77
/**
78
* Represents a writable socket
79
*/
80
interface AWritable {
81
/**
82
* Attach channel for writing so bytes written to the attached channel will be transmitted.
83
* Only one channel could be attached
84
* @param channel ByteChannel to attach for writing
85
* @returns a job that does transmit data from the channel
86
*/
87
fun attachForWriting(channel: ByteChannel): ReaderJob
88
}
89
90
/**
91
* Open a write channel, could be opened only once
92
* @param autoFlush whether returned channel do flush for every write operation
93
* @returns ByteWriteChannel for writing data to the socket
94
*/
95
fun AWritable.openWriteChannel(autoFlush: Boolean = false): ByteWriteChannel
96
```
97
98
**Usage Example:**
99
100
```kotlin
101
import io.ktor.network.sockets.*
102
import io.ktor.network.selector.*
103
import io.ktor.utils.io.*
104
105
val selectorManager = SelectorManager()
106
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
107
108
// Method 1: Open write channel directly
109
val writeChannel = socket.openWriteChannel(autoFlush = true)
110
writeChannel.writeStringUtf8("Hello, Server!")
111
// autoFlush = true means data is sent immediately
112
113
// Method 2: Manual flush control
114
val manualWriteChannel = socket.openWriteChannel(autoFlush = false)
115
manualWriteChannel.writeStringUtf8("Message 1\n")
116
manualWriteChannel.writeStringUtf8("Message 2\n")
117
manualWriteChannel.flush() // Send both messages at once
118
119
// Method 3: Attach custom channel for writing
120
val customChannel = ByteChannel()
121
val writerJob = socket.attachForWriting(customChannel)
122
123
// Write to custom channel
124
launch {
125
customChannel.writeStringUtf8("Custom channel message")
126
customChannel.flush()
127
}
128
129
// Clean up
130
writerJob.cancel()
131
socket.close()
132
```
133
134
### Connection Wrapper
135
136
Convenience class that combines a socket with its I/O channels for easier management.
137
138
```kotlin { .api }
139
/**
140
* Represents a connected socket with its input and output
141
*/
142
class Connection(
143
val socket: Socket,
144
val input: ByteReadChannel,
145
val output: ByteWriteChannel
146
)
147
148
/**
149
* Opens socket input and output channels and returns connection object
150
* @returns Connection object with socket and I/O channels
151
*/
152
fun Socket.connection(): Connection
153
```
154
155
**Usage Examples:**
156
157
```kotlin
158
import io.ktor.network.sockets.*
159
import io.ktor.network.selector.*
160
import io.ktor.utils.io.*
161
import kotlinx.coroutines.*
162
163
val selectorManager = SelectorManager()
164
165
// Simple client using Connection
166
suspend fun simpleClient() {
167
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
168
val connection = socket.connection()
169
170
// Send request
171
connection.output.writeStringUtf8("GET /api/data HTTP/1.1\r\nHost: localhost\r\n\r\n")
172
connection.output.flush()
173
174
// Read response
175
val statusLine = connection.input.readUTF8Line()
176
println("Response: $statusLine")
177
178
// Read remaining headers
179
while (true) {
180
val header = connection.input.readUTF8Line()
181
if (header.isNullOrEmpty()) break
182
println("Header: $header")
183
}
184
185
connection.socket.close()
186
}
187
188
// Echo server using Connection
189
suspend fun echoServer() {
190
val serverSocket = aSocket(selectorManager).tcp().bind("localhost", 8080)
191
192
while (true) {
193
val clientSocket = serverSocket.accept()
194
launch {
195
val connection = clientSocket.connection()
196
197
try {
198
while (true) {
199
val line = connection.input.readUTF8Line() ?: break
200
connection.output.writeStringUtf8("Echo: $line\n")
201
connection.output.flush()
202
}
203
} finally {
204
connection.socket.close()
205
}
206
}
207
}
208
}
209
210
// Bidirectional communication
211
suspend fun chatClient() {
212
val socket = aSocket(selectorManager).tcp().connect("chat-server.com", 8080)
213
val connection = socket.connection()
214
215
// Send messages
216
launch {
217
while (true) {
218
val message = readLine() ?: break
219
connection.output.writeStringUtf8("$message\n")
220
connection.output.flush()
221
}
222
}
223
224
// Receive messages
225
launch {
226
while (true) {
227
val message = connection.input.readUTF8Line() ?: break
228
println("Received: $message")
229
}
230
}
231
232
// Keep connection alive
233
connection.socket.awaitClosed()
234
}
235
```
236
237
### Advanced I/O Patterns
238
239
Advanced patterns for handling complex I/O scenarios.
240
241
**Binary Data Handling:**
242
243
```kotlin
244
import io.ktor.network.sockets.*
245
import io.ktor.network.selector.*
246
import io.ktor.utils.io.*
247
import io.ktor.utils.io.core.*
248
249
suspend fun binaryDataExample() {
250
val selectorManager = SelectorManager()
251
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
252
val connection = socket.connection()
253
254
// Send binary data
255
connection.output.writeByte(0x01)
256
connection.output.writeInt(12345)
257
connection.output.writeFloat(3.14f)
258
connection.output.flush()
259
260
// Read binary data
261
val flag = connection.input.readByte()
262
val number = connection.input.readInt()
263
val pi = connection.input.readFloat()
264
265
println("Received: flag=$flag, number=$number, pi=$pi")
266
267
connection.socket.close()
268
selectorManager.close()
269
}
270
```
271
272
**Packet-based Communication:**
273
274
```kotlin
275
import io.ktor.network.sockets.*
276
import io.ktor.network.selector.*
277
import io.ktor.utils.io.*
278
import io.ktor.utils.io.core.*
279
280
suspend fun packetBasedExample() {
281
val selectorManager = SelectorManager()
282
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
283
val connection = socket.connection()
284
285
// Send packet with length prefix
286
val message = "Hello, World!"
287
val messageBytes = message.toByteArray()
288
289
connection.output.writeInt(messageBytes.size) // Length prefix
290
connection.output.writeFully(messageBytes) // Message data
291
connection.output.flush()
292
293
// Read packet with length prefix
294
val length = connection.input.readInt()
295
val buffer = ByteArray(length)
296
connection.input.readFully(buffer)
297
val receivedMessage = buffer.decodeToString()
298
299
println("Received packet: $receivedMessage")
300
301
connection.socket.close()
302
selectorManager.close()
303
}
304
```
305
306
**Streaming Large Data:**
307
308
```kotlin
309
import io.ktor.network.sockets.*
310
import io.ktor.network.selector.*
311
import io.ktor.utils.io.*
312
import kotlinx.coroutines.*
313
import java.io.File
314
315
suspend fun streamingExample() {
316
val selectorManager = SelectorManager()
317
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080)
318
val connection = socket.connection()
319
320
// Stream file upload
321
launch {
322
val file = File("large-file.dat")
323
val buffer = ByteArray(8192) // 8KB buffer
324
325
file.inputStream().use { fileInput ->
326
while (true) {
327
val bytesRead = fileInput.read(buffer)
328
if (bytesRead == -1) break
329
330
connection.output.writeFully(buffer, 0, bytesRead)
331
connection.output.flush()
332
}
333
}
334
335
connection.output.close() // Signal end of stream
336
}
337
338
// Stream file download
339
launch {
340
val outputFile = File("downloaded-file.dat")
341
val buffer = ByteArray(8192)
342
343
outputFile.outputStream().use { fileOutput ->
344
while (true) {
345
val bytesAvailable = connection.input.readAvailable(buffer)
346
if (bytesAvailable == -1) break
347
348
fileOutput.write(buffer, 0, bytesAvailable)
349
}
350
}
351
}
352
353
connection.socket.awaitClosed()
354
selectorManager.close()
355
}
356
```
357
358
**Error Handling:**
359
360
```kotlin
361
import io.ktor.network.sockets.*
362
import io.ktor.network.selector.*
363
import io.ktor.utils.io.*
364
import kotlinx.coroutines.*
365
366
suspend fun errorHandlingExample() {
367
val selectorManager = SelectorManager()
368
369
try {
370
val socket = aSocket(selectorManager).tcp().connect("localhost", 8080) {
371
socketTimeout = 10000 // 10 second timeout
372
}
373
374
val connection = socket.connection()
375
376
// Wrap I/O operations in try-catch
377
try {
378
withTimeout(5000) { // 5 second operation timeout
379
connection.output.writeStringUtf8("Hello")
380
connection.output.flush()
381
382
val response = connection.input.readUTF8Line()
383
println("Response: $response")
384
}
385
} catch (e: SocketTimeoutException) {
386
println("Socket timeout: ${e.message}")
387
} catch (e: TimeoutCancellationException) {
388
println("Operation timeout: ${e.message}")
389
} catch (e: Exception) {
390
println("I/O error: ${e.message}")
391
} finally {
392
connection.socket.close()
393
}
394
395
} catch (e: ConnectException) {
396
println("Connection failed: ${e.message}")
397
} finally {
398
selectorManager.close()
399
}
400
}
401
```