본문 바로가기
Computer Science/Network

Java로 알아보는 TCP Socket Programming

by NewCodes 2025. 7. 23.
요약: Java를 통해 TCP Socket Programming을 점진적으로 구현하며 학습하는 글이다. 소켓 통신, 멀티 스레드의 활용, I/O Multiplexing, Reactor Pattern 등을 다룬다. 이를 통해 두 컴퓨터가 애플리케이션 수준에서 통신을 위해 어떤 과정을 거치는지 이해하고, 효율적인 I/O를 하는 방법을 고민할 수 있다.  

 

네트워크에서 Socket Programming은 빠질 수 없다. 

 

Socket Programming을 본격적으로 하기 전에 기본 개념에 대해서 알아보자. 

 


📍 개요

Socket, IP 주소, Port

Socket이란 두 컴퓨터가 통신하는 데 필요한 양 끝단의 통신 인터페이스이다. 이 Socket에는 두 가지 정보가 필수적으로 포함되어야 한다. IP 주소Port이다.

 

IP 주소란 어느 컴퓨터인지를 나타내는 네트워크 상의 주소이다.

Port는 그 컴퓨터에서 프로세스가 가지고 있는 식별 번호라고 보면 된다. 

 

IP 주소와 Port가 필요한 이유는 생각해보면 간단하다.

A 컴퓨터에서 B 컴퓨터로 데이터를 보낸다고 생각해보자. A 컴퓨터는 'B 컴퓨터가 어디있는지' 그 주소를 알아야 한다. 그리고, B 컴퓨터 안에서 여러 프로세스 중 '어떤 프로세스에 데이터가 도착'해야 하는지 알아야 한다.  

 

Socket Programming

Socket Programming이란 소켓을 통해서 두 컴퓨터가 통신하게 해주는 프로그래밍 기법이다. Socket Programming에는 두 가지 종류가 있다. TCP Socket Programming과 UDP Socket Programming이다. 

 

TCP는 연결 지향이지만, UDP는 연결 지향적이지 않은 대신에 빠른 통신을 지원하는 프로토콜이다. 연결 지향이란 두 컴퓨터가 계속해서 연결된 상태를 유지하여 통신하는 것을 의미한다. 구체적으로는 TCP에서는 연결을 위해 3 way handshake를 하지만, UDP는 그러한 연결을 위해 별도의 과정을 거치지 않는다. 

 

이렇게 TCP가 연결 지향적이며 더욱 신뢰적인 덕분에 아직까지 일반적인 웹서비스에서 TCP를 더 많이 사용한다. 이 글에서도 TCP Socket Programming에 대해서 중점적으로 알아볼 것이다. 

 

참고로 TCP의 Socket에서는 로컬 IP, 로컬 Port, 원격 IP, 원격 Port 정보가 포함되어 있다. 즉, 나의 주소 정보와 상대방의 주소 정보가 함께 들어가 있다. 

 


🎯 Echo Server

우선 간단한 에코 서버를 만들어보자. 에코 서버란 클라이언트가 보낸 데이터를 그대로 다시 응답하는 서버를 의미한다.

 

코드부터 바로 살펴보자. 

 

Server.java

Server.java와 Client.java 두 가지 파일이 있다. 서버 코드부터 살펴보자. 

package talk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

class Server {
    private static final int PORT = 8080;
    private static ServerSocket server;
    private static Socket client;

    public static void main(String[] args) throws IOException {
    	// 서버 소켓 생성 
        server = new ServerSocket(PORT);
        
        // 클라이언트 연결 대기
        listenAndAccept(); // blocking
        
        // 클라이언트와 데이터 주고 받을 수 있는 Stream 생성 
        BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter writer = new PrintWriter(client.getOutputStream(), true);
        
        // 클라이언트에게 연결 완료되었다는 메시지 전송 
        writer.println("[welcome] 연결 완료!");
        
        // 클라이언트의 메시지를 받은 이후, 그대로 응답
        String message;
        while ((message = reader.readLine()) != null) {
            writer.println("[echo]" + message);
        }
    }

    public static void listenAndAccept() throws IOException {
        client = server.accept();
    }
}

 

주석을 바탕으로 코드를 읽으면 쉽게 이해될 것이다. 하지만, 여러 낯선 클래스가 보일 것이다. 각각의 클래스가 어떤 역할을 하는지 살펴보자. 

 

  • ServerSocket
    • 서버에서 클라이언트의 연결을 기다릴 때 쓰는 Socket
    • `accpet()`: 클라이언트의 연결 요청이 들어올 때까지 Blocking하며 연결을 기다리는 함수 
    • 흔히 Listening Socket이라고 함
  • Socket
    • 연결된 이후 통신을 담당하는 Socket
    • 흔히 Connected Socket이라고 함
  • InputStreamReader
    • 바이트스트림문자스트림으로 변환해주는 역할 
  • BufferedReader
    • 버퍼를 통해 여러 문자를 한 번에 읽음으로써 효율적으로 읽게 해주는 도구 
  • PrintWriter
    • 문자열 출력을 쉽게 도와주는 도구 
    • autoFlush 지원 

중요한 건 ServerSocket과 Socket이다. 나머지는 입출력을 도와주는 도구이다. Socket 클래스는 잘 이해해두고 넘어가자. 

 

Client.java

이번엔 클라이언트 측 코드를 살펴보자. 

package talk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;

class Client {
    private static final int PORT = 8080;
    private static Socket client;

    public static void main(String[] args) throws IOException {
    	// 소켓 생성 및 연결 
        client = new Socket();
        client.connect(new InetSocketAddress("localhost", PORT));
        
        // 서버와 데이터를 주고받기 위한 스트림 생성 
        BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter writer = new PrintWriter(client.getOutputStream(), true);
        
        // 유저의 입력을 받아내기 위한 scanner 생성
        Scanner scanner = new Scanner(System.in);
        
        // 서버에서 보내주는 웰컴 메시지 받기 
        String welcome = reader.readLine();
        System.out.println(welcome);
        
        // 유저의 입력을 받아 서버에 보내기 
        String input;
        while (true) {
            input = scanner.nextLine(); // Blocking

            writer.println(input);

            String response = reader.readLine(); // Blocking
            System.out.println(response);
        }
    }
}

 

매번 유저의 입력을 받아 서버에 보내고, 돌아오는 응답을 받아내는 역할을 한다. 

 

서버 코드와 클라이언트 코드를 실행하여 통신하면, 클라이언트는 예시로 아래와 같은 결과를 받게 된다. 

Echo Server

 

정리

Echo Server와 Client를 완성시켰으니 이제 관련 개념을 정리해보자. 

 

1) 소켓 통신 흐름

소켓을 연결하여 데이터를 주고받는 과정을 자세히 살펴보자. C 언어를 기준으로 낮은 수준에서의 과정을 살펴보자. 

https://recipes4dev.tistory.com/153#google_vignette

 

socket()을 통해 socket을 생성하고, bind()를 통해 socket의 port를 지정해준다.

그리고 listen()을 통해 클라이언트의 요청을 감지하며, 요청이 오면 accept()로 받아준다. 

 

2) 두 가지 Socket

서버 측에서는 두 가지 종류의 Socket이 존재한다. 

  1. Listening Socket
    1. 클라이언트의 연결 요청을 기다리는 Socket
    2. java에서 ServerSocket에 해당함
    3. `socket() -> bind() -> listen()`의 과정으로 이루어짐
  2. Connected Socket
    1. 연결된 클라이언트와 통신을 담당한 Socket
    2. `accept()` 호출 시 새로 만들어짐 
    3. 클라이언트마다 하나씩 존재 

서버에서 소켓이 두 개 존재하는 이유는 다음과 같다. 서버는 여러 클라이언트를 상대해야 하므로, 클라이언트의 연결 요청을 accept할 수 있는 전용 Socket이 별도로 필요하기 때문이다. 반면에 클라이언트는 딱 하나의 서버와 연결되기에 Socket 하나로도 충분하다. 

 

3) 지금의 Echo Server 문제

현재의 코드에서는 한계가 있다. 서버는 오직 하나의 클라이언트와만 연결될 수 있다. 그 이유는 Server.js는 하나의 스레드를 통해 실행되고, 하나의 클라이언트와 연결된 이후로는 해당 클라이언트의 메시지를 계속해서 기다리고 있기 때문이다. 즉, 아래 while문에 갇혀있기 때문이다. 

// 클라이언트의 메시지를 받은 이후, 그대로 응답
String message;
while ((message = reader.readLine()) != null) {
    writer.println("[echo]" + message);
}

 

그러니 이 상태에서 새로운 클라이언트가 연결하려 해도 받아줄 수 없는 것이다. 이 문제를 해결해보자. 
 


🌈 Multi-Thread를 활용한 Echo Server

위 문제는 간단하게 해결할 수 있다. 스레드를 여러 개 사용하면 된다. 이때 여러 스레드를 둘로 구분할 수 있다.

  1. 새로운 클라이언트의 연결 요청을 받아주는 스레드
  2. 연결된 클라이언트와 데이터를 주고 받는 스레드

바로 코드로 살펴보자. 

package talk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Server {
    private static final int PORT = 8080;
    private static final int THREAD_POOL_SIZE = 10;
    private static ServerSocket server;
    private static Socket client;
    private static ExecutorService executor;

    public static void main(String[] args) throws IOException {
        server = new ServerSocket(PORT);
        executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE); // 스레드 풀 생성 
        
        // 새로운 클라이언트의 연결 요청 받아주면서, 연결되면 스레드 풀을 통해 메시지 주고받기
        while (true) { 
            listenAndAccept();
            executor.submit(() -> handleClinet()); 
        }
    }

    public static void handleClinet() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
            PrintWriter writer = new PrintWriter(client.getOutputStream(), true);

            writer.println("[welcome] 연결 완료!");

            String message;
            while ((message = reader.readLine()) != null) {
                writer.println("[echo]" + message);
            }
        } catch (IOException e) {
            System.err.println("클라이언트 처리 오류: " + e.getMessage());
        }
    }

    public static void listenAndAccept() throws IOException {
        client = server.accept();
    }
}

 

위 코드를 실행하면 아래처럼 여러 클라이언트와 통신이 가능해진다. 

첫 번째 클라이언트
두 번째 클라이언트

 

정리

이게 가능한 이유는 멀티스레드를 활용했기 때문이다. 하나의 스레드는 새로운 클라이언트의 요청을 받아주고, 다른 스레드는 연결된 클라이언트와 데이터를 주고받는다. 

 

멀티스레드로 구현하기 위해 Java의 concurrent 패키지의 ExecutorService를 사용했다. 

 

Java의 ExecutorService는 스레드 실행을 추상화하여 관리해주는 도구이다. 위 그림에서 보이듯이 내부적으로는 Task QueueThread Pool이 존재한다. 

 

이를 통해 복잡하게 Thread를 일일이 컨트롤하지 않아도 되는 장점이 있다. 

 

하지만 멀티스레드에도 한계는 있다.

현재는 클라이언트가 하나 연결될 때마다 스레드를 하나씩 할당하고 있다. 클라이언트가 무수히 많아질수록 스레드 수도 무수히 많아질 것이다. 

 

스레드 수가 지나치게 많아지면 치명적인 문제가 발생한다. Context Switching으로 인한 오버헤드가 커진다. Context Switching이란 CPU 제어권을 한 프로세스나 스레드에서 다른 프로세스나 스레드로 전환하는 과정이다. 

 

https://afteracademy.com/blog/what-is-context-switching-in-operating-system/

 

이러한 Context Switching은 비용이 많이 드는 작업이다. 그 이유는 기존의 상태를 저장하고, 새로운 상태를 로드하는 과정이 오래 걸리기 때문이다. 내부적으로 보면 Context를 Switch하기 위해 CPU 레지스터, 프로세스 상태, 메모리 관리 정보 등을 갈아 끼워야 하기에 그렇다. 

 

이러한 문제를 해결해보자. 멀티 스레드 말고 다른 방법을 활용해보자.  

 


📢 I/O Multiplexing를 활용한 Echo Server

위에서 언급한 문제를 해결해보자. 

 

이를 해결하기 위해서는 I/O Multiplexing을 도입해야 한다. I/O Multiplexing은 하나의 스레드로 여러 소켓을 동시에 감시하여 I/O가 준비된 소켓에 대한 작업을 효율적으로 하는 기법이다. 

 

이 역시 코드부터  바로 살펴보자. 

 

Server.java

package talk;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

class Server {
    private static final int PORT = 8080;
    private static final int BUFFER_SIZE = 1024;
    private static Selector selector;
    private static ServerSocketChannel serverChannel;

    public static void main(String[] args) throws IOException {
        selector = Selector.open(); 
        serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(PORT));
        serverChannel.configureBlocking(false);
        // Listening Socket이니 ACCEPT 이벤트 등록
        serverChannel.register(selector, SelectionKey.OP_ACCEPT); 
        
        while (true) {
            // selector에 등록한 channel 중 I/O 준비된 것 찾을 때까지 Blocking 
            selector.select(); 
            
            // 준비된 channel의 SelctionKey 가져오기 
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            
            // SelectionKey 순회하며 적절한 핸들링
            while (iterator.hasNext()) {
            	SelectionKey key = iterator.next();
                
                if (key.isAcceptable()) {
                    handleAccept();
                }
                
                if (key.isReadable()) {
                    handleRead(key);
                }
                
                iterator.remove();
            }
        }
    }
    
    public static void handleAccept() throws IOException {
    	SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println(clientChannel.getRemoteAddress() + " 클라이언트와 연결되었습니다.");
    }
    
    public static void handleRead(SelectionKey key) throws IOException {
    	SocketChannel clientChannel = (SocketChannel) key.channel();
        
        // `buffer -> byte[] -> String`으로 변환하며 데이터 읽기 
        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
        int bytesRead = clientChannel.read(buffer);
        if (bytesRead > 0) {
            buffer.flip(); // 모드 전환(쓰기 -> 읽기)
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            String message = new String(data);
            
            // 에코 응답
            String response = "[echo]" + message;
            ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
            clientChannel.write(responseBuffer);
        } else if (bytesRead == -1) {
            System.out.println(clientChannel.getRemoteAddress() + "클라이언트가 연결을 종료했습니다.");
            key.cancel();
            clientChannel.close();
        }
    }
}

 

이 또한 주석을 따라가며 읽다보면 낯설긴 하지만 대략적인 흐름은 보일 것이다. 이 코드를 제대로 이해하기 위해서는 SocketChannelSelector에 대해서 알아야 한다. 이 둘은 Java의 NIO(New I/O)에 포함된 클래스이다. 

 

https://braindisk.tistory.com/131

 

  • SocketChannel
    • Selector와 함께 쓰이며 소켓에 연결하여 데이터를 읽고 쓰는 역할
    • buffer를 통해서 논블로킹 방식으로 데이터를 읽고 씀  
    • TCP 소켓 구현체이며, UDP에서는 DatagramChannel을 사용함
    • ServerSocketChannel은 Listening Socket으로서의 역할을 함
  • Selector
    • 하나의 스레드를 통해 여러 Channel에서의 I/O 이벤트를 감지하는 역할
  • SelectionKey
    • Channel과 Selector 간의 연결을 도와주는 객체
    • 어떤 관심 이벤트를 설정할 것인지 명시함
    • 이를 통해 현재 준비된 이벤트가 무엇인지 확인 가능

 

Channel은 말 그대로 다른 컴퓨터와 통신할 수 있는 채널 역할을 한다고 이해하면 된다.

Selector는 Channel을 감시하여 I/O가 준비된 Channel을 고른다는 의미로 이해하면 된다. 

 

이를 통해 별도의 스레드를 할당하지 않고 하나의 스레드로도 여러 클라이언트와 연결하여 통신을 주고받을 수 있다. 

 

Client.java

이런 서버와 통신을 주고받기 위해서 Client는 어떻게 코드를 작성해야 하는지 살펴보자. 

package talk;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.nio.channels.SocketChannel;
import java.nio.ByteBuffer;

class Client {
    private static final String HOST = "localhost";
    private static final int PORT = 8080;
    private static final int BUFFER_SIZE = 1024;
    private static Scanner scanner;

    public static void main(String[] args) throws IOException {
    	scanner = new Scanner(System.in);
    	SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress(HOST, PORT));
        System.out.println("서버와 연결 완료!");
        
        while (true) {
            // 유저의 입력 받기 
            String message = scanner.nextLine(); 
            // `String -> Byte[] -> Buffer`로 변환하여 전송
            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); 
            socketChannel.write(buffer);
            
            // 에코 응답 받기
            ByteBuffer responseBuffer = ByteBuffer.allocate(BUFFER_SIZE);
            int bytesRead = socketChannel.read(responseBuffer);
            if (bytesRead > 0) {
                responseBuffer.flip();
                byte[] data = new byte[responseBuffer.remaining()];
                responseBuffer.get(data);
                String response = new String(data);
                System.out.println(response);
            }
        }
    }
}

 

Server.java 코드를 잘 이해했다면 Client.java를 이해하는 데 큰 문제가 없을 것이다. 

 

위 코드를 실행하면 아래와 같이 출력되는 걸 볼 수 있다. 

서버
첫 번째 클라이언트
두 번째 클라이언트

 

서버가 이제 2개의 클라이언트도 잘 받아준다!

 

정리

1) I/O Multiplexing

https://blog.naver.com/manhdh/120164273361

 

이번의 주요 변화는 I/O Multiplexing 기법을 도입한 점이었다. 하나의 스레드를 통해서 여러 소켓(채널)을 감시하게 하는 방법이다. 이를 통해 적은 스레드로도 여러 클라이언트와 통신을 주고받을 수 있다. 

 

I/O Mulitplexing을 구현하는 데 도와준 건 Java의 NIO인 SocketChannel과 Selector이다. SocketChannel은 소켓에 연결하여 데이터를 읽고 쓰는 창구 역할을 한다. Selector는 하나의 스레드를 통해 여러 Channel에서의 I/O 이벤트를 감시하는 역할을 한다. 

 

해당 클래스는 Netty에서도 쓰인다. Netty는 Java의 비동기 및 이벤트 기반의 자바 프레임워크이다. 참고로 이 Netty는 Spring WebFlux 내부에서 쓰인다. 즉, 위 소켓 프로그래밍을 잘 이해하면 향후 고성능의 비동기, 이벤트 기반 애플리케이션 코드를 작성하는 데 도움이 될 것이다. 

 

2) Reactor Pattern

서버 코드의 흐름을 보면 하나의 스레드를 통해 이벤트가 발생했는지 확인하고, 발생했다면 적절한 핸들러를 호출했다. 이러한 패턴을 Reactor Pattern이라고 한다. 

 

https://en.wikipedia.org/wiki/Reactor_pattern

  • Event Handler
    • 요청을 처리하기 위한 구체적인 로직을 담고 있음
    • 위 서버 코드에서 봤던 `handleAccept()`, `handleRead()`가 여기에 해당함
  • Dispatcher
    • 이벤트 발생하면 적절한 event handler를 호출하는 역할
    • reactive application의 event loop
    • event handler의 등록 정보를 관리
    • 위 서버 코드에서 while문 돌면서 이벤트 발생하면 적절한 핸들러 호출해주는 부분이 여기에 해당함
  • Handle
    • 운영체제가 제공하는 인터페이스
    • 소켓, 파일 디스크립터 등이 있음 (여기서는 소켓)
    • 데이터의 송수신이 이루어지는 통로 역할 
  • Demultiplexer
    • Handle의 상태 변화를 감지하고, 변경 사항을 이벤트로 알리는 장치 

Reactor Pattern을 통해 적은 스레드로도 동시에 수많은 요청을 처리할 수 있다. 이 디자인 패턴은 Node.js, NginX, Redis, Netty 등에 녹여져 있다.

 

참고로 이러한 패턴에서는 event loop를 돌리는 main thread를 block시키지 않는 게 매우 중요하다. 이와 관련해서는 아래 글을 꼭 읽어봐야 한다. ㅎㅎ

 

Node.js에서 readFileSync를 쓰면 안 되는 이유

 

Node.js에서 readFileSync를 쓰면 안 되는 이유 (feat. Event Loop)

안녕하세요! NewCodes입니다! Node.js에서`readFileSync`를 써보신 적이 있으신가요? `readFileSync`는 특별한 경우를 제외하고써서는 안 되는 함수입니다. `readFileSync`를 쓰면주방에 있는 셰프가 멈추는 것과

newcodes.tistory.com

 


📌 마무리

요약

  1. Socket 통신 기본 개념
    1. Socket: 통신을 하기 위해 필요한 양 끝단의 통신 인터페이스
    2. IP 주소: 한 컴퓨터의 네트워크상 주소 
    3. Port: 한 컴퓨터의 여러 프로세스 중 한 프로세스의 식별 번호
    4. Listening Socket: 클라이언트의 소켓 연결을 대기하며 accept해주는 socket 
    5. Connected Socket: 클라이언트와 데이터를 주고받는 socket
  2. Java
    1. ExecutorService: thread pool과 task queue를 통해 멀티 스레드 사용을 추상화한 도구 
    2. SocketChannel: Java NIO에서 Buffer를 통해 데이터를 읽고 쓰는 도구 
    3. Selector: 등록된 Channel을 감시하며 I/O 작업이 완료된 Channel을 알려주는 도구 
  3. I/O Multiplexing: 하나의 스레드로 여러 소켓을 동시에 감시하여 I/O가 준비된 소켓에 대한 작업을 효율적으로 하는 기법 
  4. Reactor Pattern: Event Loop와 핸들러를 활용하여 여러 요청을 동시에 핸들링하는 패턴 

 

퀴즈

  1. Socket은 IP 주소와 Port 정보를 필수로 지니고 있다. (O/X)
  2. Server와 Client가 서로 Socket을 연결하고 통신하기 위해서 총 2개의 Socket이 필요하다. (O/X) 
  3. SocketChannel은 I/O 작업이 완료된 Channel을 알려주는 도구이다. (O/X)
  4. Reactor Pattern을 쓰는 기술은 Netty, Node.js, Redis 등이 있다. (O/X)
  5. 멀티 스레드를 쓰는 소켓 서버에서 각 스레드는 하나의 소켓을 가지고 있다. 그렇다면 각 스레드의 포트 번호는 몇 번이 할당될까? 

 

 

 

 

 

 

 

퀴즈를 풀어봅시다!!!!

아래에 정답이 있습니다!!!!

 

 

 

 

 

 

 

 

 

퀴즈 정답

  1. Socket은 IP 주소와 Port 정보를 필수로 지니고 있다. (O/X)
    1. O
    2. 두 컴퓨터가 서로 통신을 하기 위해서는 컴퓨터의 위치, 프로세스의 위치를 알아야 한다. 
  2. Server와 Client가 서로 Socket을 연결하고 통신하기 위해서 총 2개의 Socket이 필요하다. (O/X) 
    1. X
    2. 총 3개의 Socket이 필요하다. Server의 Listening Socket, Connected Socket과 Client의 Connected Socket이 필요하다. 
  3. SocketChannel은 I/O 작업이 완료된 Channel을 알려주는 도구이다. (O/X)
    1. X
    2. 해당 설명은 Selector에 해당한다. 
  4. Reactor Pattern을 쓰는 기술은 Netty, Node.js, Redis 등이 있다. (O/X)
    1. O
  5. 멀티 스레드를 쓰는 소켓 서버에서 각 스레드는 하나의 소켓을 가지고 있다. 그렇다면 각 스레드의 포트 번호는 몇 번이 할당될까? 
    1. 포트 번호는 동일하다. 각 스레드에 새로운 포트 번호가 할당되는 게 아니다. 포트 번호는 각 프로세스에게 부여되는 식별 번호라고 했었다. 스레드는 개념적으로 프로세스에 종속되기에 같은 포트 번호인 게 맞다. 

 

레퍼런스

 

반응형