Skip to content

echo server を書いてみる

POSIX API を直接利用して、echo server を記述することを考えます。

設定

build.gradle.kts に以下のように追記します。

kotlin
plugins {
    kotlin("multiplatform") version "2.0.21"
}

kotlin {
    val isAarch64 = System.getProperty("os.arch").contains("aarch64")
    if (isAarch64) {
        macosArm64("native") {
            binaries {
                executable()
            }
        }
    } else {
        macosX64("native") {
            binaries {
                executable()
            }
        }
    }

    sourceSets {
    }
}

実装

kotlin
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.alloc
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.refTo
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.sizeOf
import kotlinx.cinterop.value
import platform.posix.AF_INET
import platform.posix.INADDR_ANY
import platform.posix.SOCK_STREAM
import platform.posix.accept
import platform.posix.bind
import platform.posix.close
import platform.posix.listen
import platform.posix.memset
import platform.posix.perror
import platform.posix.posix_htons
import platform.posix.recv
import platform.posix.send
import platform.posix.sockaddr_in
import platform.posix.socket
import platform.posix.socklen_tVar

@OptIn(ExperimentalForeignApi::class)
fun main() {
    val serverSocket = socket(AF_INET, SOCK_STREAM, 0)
    if (serverSocket == -1) {
        perror("socket")
        return
    }

    val port: Short = 12345

    memScoped {
        val serverAddr = alloc<sockaddr_in>()
        memset(serverAddr.ptr, 0, sizeOf<sockaddr_in>().toULong())
        serverAddr.sin_family = AF_INET.convert()
        serverAddr.sin_port = posix_htons(port).toUShort()
        serverAddr.sin_addr.s_addr = INADDR_ANY

        if (bind(serverSocket, serverAddr.ptr.reinterpret(), sizeOf<sockaddr_in>().convert()) == -1) {
            perror("bind")
            return
        }

        if (listen(serverSocket, 10) == -1) {
            perror("listen")
            return
        }

        println("Server is listening on port 12345")

        while (true) {
            val clientAddr = alloc<sockaddr_in>()
            val clientAddrLen = alloc<socklen_tVar>()
            clientAddrLen.value = sizeOf<sockaddr_in>().convert()

            val clientSocket = accept(serverSocket, clientAddr.ptr.reinterpret(), clientAddrLen.ptr)
            if (clientSocket == -1) {
                perror("accept")
                continue
            }

            println("Client connected")

            val buffer = ByteArray(1024)
            while (true) {
                val bytesRead = recv(clientSocket, buffer.refTo(0), buffer.size.convert(), 0)
                if (bytesRead <= 0) {
                    if (bytesRead.toInt() == 0) {
                        println("Client disconnected")
                    } else {
                        perror("recv")
                    }
                    break
                }
                send(clientSocket, buffer.refTo(0), bytesRead.convert(), 0)
            }

            close(clientSocket)
        }
    }

    close(serverSocket)
}

ちょっと import が多いので、ダルい感じになっているが、ほぼ C で書いたときのコードと同じです。 C で書いた場合に比べると、IDEA で書けるから書きやすい。ぐらいでそんなに差はないですね。