Java & Kotlin

Socket 통신

백엔드 유성 2024. 12. 15. 00:01

1. Socket 통신이란 무엇인가?

Socket은 네트워크 프로그래밍에서 가장 기본적이고 중요한 개념 중 하나입니다. 다양한 네트워크 애플리케이션(채팅, 실시간 게임, 파일 전송 등)을 개발하려면 클라이언트-서버 간 통신이 필수적입니다. 이때 Socket은 데이터를 주고받는 양 끝단의 연결점을 의미합니다.

 

2. Socket 통신의 기본 개념

2.1. Socket이란?

  • Socket네트워크 상의 두 호스트 간 통신을 위한 인터페이스입니다.
  • 주로 TCP/IP 또는 UDP 프로토콜 위에서 데이터를 주고받을 때 사용됩니다.

 

2.2. TCP와 UDP의 차이

구분 TCP UDP
프로토콜 타입 연결형(Connection-oriented) 비연결형(Connectionless)
신뢰성 데이터 무결성 및 순서 보장 신뢰성 보장 안 함
속도 느림 빠름
데이터 전송 단위 바이트 스트림 메시지 단위
사용 예시 HTTP, FTP, SMTP HTTP3, 실시간 게임, 스트리밍, DNS

 

3. Java의 Socket 통신 기초

3.1. 클라이언트와 서버 개념

  • 클라이언트: 연결을 요청하고 데이터를 전송합니다.
  • 서버: 클라이언트의 연결 요청을 수락하고 데이터를 처리합니다.

 

3.2. TCP 소켓 클래스

Java에서 TCP 소켓 통신을 위해 다음 클래스들이 제공됩니다:

  • Socket: 클라이언트에서 사용 (연결 및 데이터 송신/수신).
  • ServerSocket: 서버에서 사용 (클라이언트의 연결 요청을 기다림).

 

4. 간단한 TCP 소켓 통신 예제

4.1. 서버 코드

import java.io.*;
import java.net.*;

public class SimpleServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            System.out.println("서버 대기 중...");

            while (true) {
                Socket clientSocket = serverSocket.accept(); // 클라이언트 연결 수락
                System.out.println("클라이언트 연결됨: " + clientSocket.getInetAddress());

                BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);

                String clientMessage = reader.readLine();
                System.out.println("수신 메시지: " + clientMessage);

                writer.println("서버 응답: " + clientMessage); // 클라이언트에게 응답 전송
                clientSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

4.2. 클라이언트 코드

import java.io.*;
import java.net.*;

public class SimpleClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 8080)) {
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            writer.println("Hello, Server!"); // 서버로 메시지 전송
            String serverResponse = reader.readLine();
            System.out.println("서버 응답: " + serverResponse);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

5. Socket 통신에서 발생하는 주요 문제와 해결 방법

5.1. 메시지 경계 문제

TCP는 바이트 스트림 기반이므로 메시지의 경계가 자동으로 구분되지 않습니다.

해결 방법:

  1. 구분자 사용: 메시지 끝에 \n이나 특수 문자를 추가.
  2. 길이 프레임 추가: 메시지 앞에 길이 정보를 추가.
  3. 직렬화: ObjectOutputStream/ObjectInputStream을 사용.

5.2. 대량 요청 처리

대량의 클라이언트 요청이 몰릴 경우, 서버는 스레드 풀을 사용해 효율적으로 처리해야 합니다.

해결 방법

  • 스레드 풀 (ExecutorService)을 사용해 동시 요청 처리.
  • 비동기 처리 (NIO 또는 Netty 사용).

 

5.3. 연결 유지/끊김 감지

클라이언트와 서버 간 네트워크 연결이 끊기면 문제가 발생할 수 있습니다.

해결 방법

  • Keep-Alive 옵션 사용: 소켓 연결 유지.
  • Heartbeat 구현: 주기적으로 “ping” 메시지를 보내 연결 상태 확인.

 

6. 고급 개념: NIO와 비동기 소켓

6.1. NIO란?

  • NIO (Non-blocking I/O)는 Java 1.4부터 도입된 비동기 소켓을 위한 API입니다.
  • SocketChannel, ServerSocketChannel, Selector 등을 사용해 성능을 최적화합니다.

 

6.2. Netty 프레임워크

  • Netty는 NIO를 기반으로 만든 네트워크 애플리케이션 프레임워크입니다.
  • 높은 성능과 확장성을 제공하며, TCP/UDP 기반의 애플리케이션에 많이 사용됩니다.

 

7. 실전에서의 Socket 사용 사례

  1. 온라인 게임: 실시간 상태 업데이트 및 이벤트 전송.
  2. 메신저/채팅 서비스: 즉시성 메시지 전송.
  3. 주식 거래 시스템: 빠른 속도의 데이터 전송.
  4. 파일 전송 애플리케이션: 대용량 데이터 전송.
  5. 스트리밍 서비스: 지속적인 데이터 흐름 처리.