01
03

์˜ค๋Š˜ ๋ถ€ํ„ฐ ์˜คํ”ˆ์†Œ์Šค ์›น ํ†ต์‹  ํ”„๋ ˆ์ž„ ์›Œํฌ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์—ฌ์ •์„ ๋– ๋‚˜๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ผ๋‹จ ์ œ์ผ ๋จผ์ € TCP ํ†ต์‹ ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด๋ฉฐ, ๋„คํŠธ์›Œํฌ ์ง€์‹์„ ๋” ๊นŠ๊ฒŒ ๊ฒฝํ—˜ํ•ด๋ณด๋Š” ๊ฒŒ ์ฒซ๋ฒˆ์งธ ๋ชฉํ‘œ๋‹ค.

 

# TCP

TCP(Transmission Control Protocol)๋Š” ์ธํ„ฐ๋„ท ํ”„๋กœํ† ์ฝœ ์Šค์œ„ํŠธ์˜ ํ•ต์‹ฌ ํ”„๋กœํ† ์ฝœ๋กœ, ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก์„ ๋ณด์žฅํ•œ๋‹ค. ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก์ด๋ผ ํ•˜๋ฉด, ๋ณด๋‚ด๊ณ  ๋ฐ›์•˜๋‹ค๋Š” ๊ฒŒ ํ™•์ธ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์—ฐ๊ฒฐํ•  ๋•Œ๋Š” 3-way handshake, ์—ฐ๊ฒฐ์„ ์ข…๋ฃŒํ•  ๋•Œ๋Š” 4-way handshake๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.

sequenceDiagram
    participant C as Client
    participant S as Server
    
    Note over C,S: 3-Way Handshake (์—ฐ๊ฒฐ ์ˆ˜๋ฆฝ)
    C->>S: SYN (seq=x)
    Note right of C: ํด๋ผ์ด์–ธํŠธ: ์—ฐ๊ฒฐ ์š”์ฒญ
    S->>C: SYN + ACK (seq=y, ack=x+1)
    Note left of S: ์„œ๋ฒ„: ์š”์ฒญ ์ˆ˜๋ฝ & ๋™๊ธฐํ™”
    C->>S: ACK (ack=y+1)
    Note right of C: ํด๋ผ์ด์–ธํŠธ: ์—ฐ๊ฒฐ ํ™•์ธ
    
    Note over C,S: ๋ฐ์ดํ„ฐ ์ „์†ก
    C->>S: Data Transfer
    S->>C: ACK
    S->>C: Data Transfer
    C->>S: ACK
    
    Note over C,S: 4-Way Handshake (์—ฐ๊ฒฐ ์ข…๋ฃŒ)
    C->>S: FIN
    Note right of C: ํด๋ผ์ด์–ธํŠธ: ์—ฐ๊ฒฐ ์ข…๋ฃŒ ์š”์ฒญ
    S->>C: ACK
    Note left of S: ์„œ๋ฒ„: ์ข…๋ฃŒ ์š”์ฒญ ํ™•์ธ
    S->>C: FIN
    Note left of S: ์„œ๋ฒ„: ์„œ๋ฒ„๋„ ์ข…๋ฃŒ ์ค€๋น„ ์™„๋ฃŒ
    C->>S: ACK
    Note right of C: ํด๋ผ์ด์–ธํŠธ: ์ข…๋ฃŒ ํ™•์ธ

3way handshake์˜ ๊ฒฝ์šฐ์—๋Š” ์—ฐ๊ฒฐ ์‹œ๋„, ์—ฐ๊ฒฐ ํ™•์ธ ๋ฐ ์Šน์ธ ์š”์ฒญ, ์ตœ์ข… ํ™•์ธ ๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค.

SYN์„ C-> S๋กœ ์ „๋‹ฌํ•˜๊ณ , ์‹œํ€€์Šค ๋ฒˆํ˜ธ๋ž‘ ๊ฐ™์ด ๋‹ด์•„์„œ ๋ณด๋‚ธ๋‹ค. ์„œ๋ฒ„๋Š” SYN๊ณผ ๋ฐ›์€ ์‹œํ€€์Šค ๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ง€๊ณ , ์„œ๋ฒ„ ์ชฝ ์‹œํ€€์Šค ๋ฒˆํ˜ธ์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ€ ์ค€ ์‹œํ€€์Šค ๋ฒˆํ˜ธ์— +1 ํ•ด์„œ ACK์œผ๋กœ ๋ณด๋‚ธ๋‹ค. ๋งŒ๋“ค์–ด์ง„ ACK + ํด๋ผ์ด์–ธํŠธ ๊ฐ€ ์ค€ SYN์„ ๋‹ค์‹œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋‚ด๋ฉด ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ์ด๋ฒˆ์—๋Š” ์„œ๋ฒ„์˜ ์‹œํ€€์Šค ๋ฒˆํ˜ธ์— 1์„ ๋”ํ•ด ACK๋กœ ์‘๋‹ตํ•ด์ค€๋‹ค. ์ด๊ฒŒ ์„œ๋ฒ„์— ๋„์ฐฉํ•˜๋ฉด ์—ฐ๊ฒฐ์ด ์„ฑ๋ฆฝ๋œ๋‹ค.

 

4way handshake๋Š” ์กฐ๊ธˆ ๋ณต์žกํ•œ๋ฐ ์‹ ๋ขฐ์„ฑ์ด๋ผ๋Š” ๊ฐœ๋…์„ ๋“ค๊ณ ๊ฐ€๋ฉด ๋˜‘๊ฐ™๋‹ค.

์‹œ์ž‘์€ ์—ฌ์ „ํžˆ ํด๋ผ์ด์–ธํŠธ๋‹ค. ์—ฐ๊ฒฐ์„ ์ข…๋ฃŒํ•˜๊ฒ ๋‹ค๊ณ  FIN์„ ๋ณด๋‚ธ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ๋ฐ›์•˜์Œ์„ ์•Œ๋ ค์ฃผ๋Š” ACK๋ฅผ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„ ์ž์ฒด๋„ ์ข…๋ฃŒํ•˜๊ฒ ๋‹ค๊ณ  FIN์„ ๋ณด๋‚ธ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„์˜ ์ข…๋ฃŒ ์‘๋‹ต์„ ๋ฐ›๊ณ  ACK๋ฅผ ๋‹ค์‹œ ์„œ๋ฒ„์— ์ „ํ•ด์ฃผ๋ฉฐ ์—ฐ๊ฒฐ์ด ์ข…๋ฃŒ๋œ๋‹ค. ์–‘์ชฝ ๋ชจ๋‘ ๋”์ด์ƒ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋‹ˆ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•ด์•ผํ•˜๋Š”๋ฐ, ์ƒํƒœํ™•์ธ์„ ํ•œ๋‹ค์Œ์— ๋Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์•ˆ์ •์„ฑ์„ ํ™•๋ณดํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์‹œํ€€์Šค ๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋‹ˆ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด์ ์ด UDP ํ†ต์‹ ๊ณผ ๋‹ค๋ฅธ ๊ฒƒ์ด๋‹ค.

ํ๋ฆ„์ œ์–ด๋„ ๊ฐ€๋Šฅํ•˜๊ณ , ํ˜ผ์žก ์ œ์–ด๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ํŒจํ‚ท์˜ ์ฒดํฌ์„ฌ์„ ์‚ฌ์šฉํ•ด ์˜ค๋ฅ˜ ๊ฒ€์ถœ๊ณผ ์†์ƒ ํŒจํ‚ท ์žฌ์ „์†ก ๊ธฐ๋Šฅ๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ํ†ต์‹ ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 

interface TcpServer { // ๋ผ์ดํ”„์‚ฌ์ดํด ๊ด€๋ฆฌ์šฉ ์ธํ„ฐํŽ˜์ด์Šค
    fun start(port: Int)
    fun stop()
    fun isRunning(): Boolean
}

์‹œ์ž‘, ์ข…๋ฃŒ, ์ƒํƒœํ™•์ธ์šฉ ํ•จ์ˆ˜๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋งŒ๋“ค์–ด ์‹œ์ž‘ํ–ˆ๋‹ค.

private val isRunning = AtomicBoolean(false)
private var serverSocket: ServerSocket? = null
private val threadPool = Executors.newCachedThreadPool()

์†Œ์ผ“ ํ†ต์‹ ์˜ ๊ตฌํ˜„์ฒด๋กœ๋Š” ServerSocket์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. AtomicBoolean๊ฐ€ ๋ฉ€ํ‹ฐ์Šค๋ ˆํŠธ ํ™˜๊ฒฝ์—์„œ race condition ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•ด์ค€๋‹ค๊ณ  ํ•ด์„œ ์ด๊ฑธ ์ƒํƒœ ํ™•์ธ ๋ณ€์ˆ˜๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•  ์“ฐ๋ ˆ๋“œ ํ’€์„ ๋งŒ๋“ค์–ด ์ฃผ๋ฉด ๋๋‚œ๋‹ค.

cacheThreadPool์ด๋ผ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , 60์ดˆ๊ฐ„ ์‚ฌ์šฉ๋˜์ง€์•Š๋Š” ์Šค๋ ˆ๋“œ๋Š” ์ œ๊ฑฐํ•ด์ค€๋‹ค.

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}

start๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•œ๋‹ค.

override fun start(port: Int) {
    if (isRunning.get()) {
        throw IllegalStateException("Server is already running")
    }

    serverSocket = ServerSocket().apply {
        bind(InetSocketAddress(port), backlog)
    }
    isRunning.set(true)

    threadPool.submit {
        while (isRunning.get()) {
            try {
                val clientSocket = serverSocket?.accept() ?: break
                handleNewConnection(clientSocket)
            } catch (e: Exception) {
                if (isRunning.get()) {
                    e.printStackTrace()
                }
            }
        }
    }
}

private fun handleNewConnection(clientSocket: Socket) {
    threadPool.submit {
        try {
            connectionHandler.handle(clientSocket)
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            try {
                clientSocket.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

ServerSocket๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•˜๋Š” ๊ฒŒ TCP/IP๊ณ„์ธต์—์„œ LISTEN์ƒํƒœ๋กœ ์ง„์ž…ํ•˜๋Š” TCP ์—ฐ๊ฒฐ์˜ ์‹œ์ž‘์ ์ด๋‹ค. OS๊ฐ€ ์ €๊ธฐ์„œ ๋ฐ›๋Š” port์— ๋Œ€ํ•œ TCP ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  baklog์—๋Š” ๋Œ€๊ธฐ์—ด์„ ์ฒ˜๋ฆฌํ•  TCP์˜ SYN ํ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค. ์ด๊ฑธ ๋ฐฑํ”„๋ ˆ์…”๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

SYN ํ์— ์™„๋ฃŒ๋˜์ง€์•Š์€ ์—ฐ๊ฒฐ์š”์ฒญ๋“ค์ด ์Œ“์—ฌ์žˆ์„ ์˜ˆ์ •์ด๋‹ค. 

public Socket accept() throws IOException {
    if (isClosed())
        throw new SocketException("Socket is closed");
    if (!isBound())
        throw new SocketException("Socket is not bound yet");
    Socket s = new Socket((SocketImpl) null);
    implAccept(s);
    return s;
}

์‹คํ–‰์ค‘์ด๋ผ๋ฉด ๊ณ„์† ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ `serverSocket?.accept()`๋ถ€๋ถ„์ด 3way handshake๊ฐ€ ์ผ์–ด๋‚˜๋Š” ๋ถ€๋ถ„์ด๋‹ค. accept ์ž์ฒด๊ฐ€ ์—ฐ๊ฒฐ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ ๋ฐ˜ํ™˜๋œ๋‹ค.

 

TCP์—ฐ๊ฒฐ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ๊ทธ ์—ฐ๊ฒฐํ•œ ์†Œ์ผ“์—์„œ ํ†ต์‹ ์„ ์ด๋ฃฌ๋‹ค. ์ปค๋„ฅ์…˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ๋‚˜๋ฆ„์ธ๊ฑธ๋กœ ํ•ด๋‘์—ˆ๋Š”๋ฐ ์ผ๋‹จ ๋‹จ์ˆœ ์—์ฝ”์šฉ์œผ๋กœ๋งŒ ์ž‘์„ฑํ–ˆ๋‹ค. TCP์ด ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋™์ž‘ํ•˜๋‹ˆ๊นŒ inputstream, outputstream์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

class EchoConnectionHandler : ConnectionHandler {
    override fun handle(socket: Socket) {
        val input = socket.getInputStream().bufferedReader()
        val output = socket.getOutputStream().bufferedWriter()

        input.lineSequence().forEach { line ->
            output.write("$line\n")
            output.flush()
        }
    }
}

์ปค๋„ฅ์…˜ ๋ฉ”์„œ๋“œ์—์„œ์˜ `clientSocket.close()` ๋Š” accept์™€ ๋ฐ˜๋Œ€๋กœ 4way handshake๋ฅผ ๋ฐœ๋™์‹œํ‚จ๋‹ค.

telnet์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๋ฉด, ํ•œ๊ธ€์€ ๊นจ์ ธ์„œ ์˜ค์ง€๋งŒ ์ผ๋‹จ ์ž…๋ ฅํ•œ๊ฒŒ ์‘๋‹ต์œผ๋กœ ๋Œ์•„์˜ค๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์ง€๊ธˆ ๊ตฌํ˜„ํ•ด๋‘”๊ฑด ์“ฐ๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜๊ธด ํ–ˆ์ง€๋งŒ blocking I/O ๋ฐฉ์‹์ด๋ผ ๊ฐ ์—ฐ๊ฒฐ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ํ•„์š”๋กœํ•œ๋‹ค. ์ปค๋„ฅ์…˜์ด ์—ด๋ฆฌ๋Š” ๋งŒํผ ์Šค๋ ˆ๋“œ๋ฅผ ์—ด์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ ๋น„์šฉ์ด 1MB ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ์ธ๊ฑธ ๊ฐ์•ˆํ–ˆ์„ ๋•Œ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

# ๋ธ”๋กœํ‚น, ๋…ผ๋ธ”๋กœํ‚น, ๋™๊ธฐ, ๋น„๋™๊ธฐ

์ผ๋‹จ ๋ธ”๋กœํ‚น I/O๋ž‘ ๋…ผ๋ธ”๋กœํ‚น I/O์˜ ์ฐจ์ด์ ์„ ์‚ดํŽด๋ณด์ž. ๋งค์šฐ ํ—ท๊ฐˆ๋ฆฌ๋Š” ๊ฐœ๋…๋“ค์ธ๋ฐ, ๋ธ”๋กœํ‚น/๋…ผ๋ธ”๋กœํ‚น๊ณผ ๋™๊ธฐ/๋น„๋™๊ธฐ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐœ๋…์ธ๊ฑธ ์•Œ๊ณ  ๋“ค์–ด๊ฐ€์•ผ๋œ๋‹ค.

 

๋ธ”๋กœํ‚น, ๋…ผ๋ธ”๋กœํ‚น์€ ํ˜ธ์ถœ ํ•จ์ˆ˜๊ฐ€ ์ œ์–ด๊ถŒ์„ ๋ฐ”๋กœ ๋Œ๋ ค์ฃผ๋Š” ์ง€, ์•„๋‹ˆ๋ฉด ๋Œ€๊ธฐ ์‹œํ‚ค๋Š” ์ง€์— ๋Œ€ํ•œ ์ฐจ์ด๊ณ , ๋™๊ธฐ ๋น„๋™๊ธฐ๋Š” ์ž‘์—…์™„๋ฃŒ์˜ ํ†ต์ง€ ๋ฐฉ์‹ ์ฐจ์ด๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๊ทธ๋ž˜์„œ ๋™๊ธฐ๋Š” ์ง์ ‘ ์™„๋ฃŒ๊ฐ€ ๋๋‹ค๊ณ  ํ†ต์ง€๋ฅผ ํ•ด์ฃผ๊ณ , ๋น„๋™๊ธฐ๋Š” ์ฝœ๋ฐฑํ˜•์‹์œผ๋กœ ํ†ต์ง€๋ฅผ ํ•ด์„œ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ถ„๋ฆฌ์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

 

์˜ˆ์‹œ๋ฅผ ์นดํŽ˜๋กœ ๋“ค์–ด๋ณด๋ฉด ์ข€ ๋” ์ดํ•ด๊ฐ€ ์ž˜ ๋œ๋‹ค. ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•˜๋Š” ๊ฒŒ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ฒ ๋‹ค.

  • ๋ธ”๋กœํ‚น์€ ์ฃผ๋ฌธ ๋ฐ›์•˜์œผ๋‹ˆ ์นด์šดํ„ฐ์—์„œ ์ปคํ”ผ ๊ธฐ๋‹ค๋ฆฌ๋ผ๊ณ  ์‹œํ‚ค๋Š” ๊ฒƒ
  • ๋…ผ๋ธ”๋กœํ‚น์€ ์ง„๋™๋ฒจ์„ ์ฃผ๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ
  • ๋™๊ธฐ๋Š” ์ปคํ”ผ๊ฐ€ ๋‚˜์™”์„ ๋•Œ ์‚ฌ๋žŒ์ด ์ง์ ‘ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ(์•Œ๋ ค์ค„ ๋•Œ ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ๋œ๋‹ค)
  • ๋น„๋™๊ธฐ๋Š” ์ง„๋™๋ฒจ์ด ์šธ๋ ธ์„ ๋•Œ ์ฐพ์•„๊ฐ€๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ

์ด๋ ‡๊ฒŒ 4๊ฐ€์ง€๋กœ ์ •๋ฆฌ ํ•˜๊ฒ ๋‹ค.

 

์œ„์—์„œ ๋‚ด๊ฐ€ Tcp์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฐฉ์‹์€ ๋ธ”๋กœํ‚น ๋ฐฉ์‹์ด๋‹ค. ๊ฐ ์ปค๋„ฅ์…˜ ๋งˆ๋‹ค ์Šค๋ ˆ๋“œ๋ฅผ ํ• ๋‹นํ•ด์„œ ํ˜ธ์ถœ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.

 

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT