코드 한 줄의 기록

Java 소켓과 HTTP 통신: 기초부터 클라이언트 구현까지 완벽 정리 본문

JAVA

Java 소켓과 HTTP 통신: 기초부터 클라이언트 구현까지 완벽 정리

CodeByJin 2025. 11. 8. 10:25
반응형

네트워크 프로그래밍을 배우면서 "소켓"이라는 용어를 처음 접하는 개발자들은 이게 정확히 뭔지, HTTP와는 어떻게 다른지 헷갈리는 경우가 많습니다. 저도 처음엔 그랬거든요. 하지만 현장에서 실제로 서버와 클라이언트를 만들어보면서 이 두 개념의 차이가 얼마나 중요한지 깨달았습니다.

 

이 글에서는 순수 소켓 프로그래밍부터 시작해서, 현대적인 HTTP 클라이언트 구현까지 단계별로 살펴보겠습니다. 이론만 아니라 실제로 동작하는 코드를 통해, 내가 배운 걸 다른 개발자들도 쉽게 이해할 수 있도록 정리해봤습니다.

소켓(Socket)이란 무엇인가?

먼저 가장 기본적인 질문부터 답해봅시다. 소켓은 정확히 뭘까요?

 

소켓은 네트워크상에서 두 프로그램 간의 통신을 가능하게 해주는 끝점(endpoint)입니다. 더 정확하게는, IP 주소와 포트 번호로 식별되는 네트워크 통신 지점이라고 생각하면 됩니다.

 

예를 들어 생각해보면, 내 컴퓨터에서 서버로 데이터를 보낸다고 하면

  • 내 IP 주소: 192.168.1.10
  • 내 포트: 12345 (임시로 할당된 클라이언트 포트)
  • 서버 IP: 192.168.1.100
  • 서버 포트: 8080

이런 식으로 '출발지:포트'와 '목적지:포트'의 조합이 하나의 소켓을 형성하는 거죠.

소켓의 종류: TCP와 UDP

소켓 통신에는 두 가지 방식이 있습니다.

  • TCP(Transmission Control Protocol)는 연결 기반의 신뢰성 있는 통신을 제공합니다. 데이터가 반드시 순서대로 도착하며, 손실되지 않도록 보장합니다. 3-way handshake라는 연결 과정을 거쳐야 하지만, 안정성이 최우선인 상황에 적합합니다. 웹 서버, 파일 전송, 데이터베이스 연결 등에서 사용됩니다.
  • UDP(User Datagram Protocol)은 비연결 기반으로 빠른 전송에 중점을 두고 있습니다. 연결 과정이 없어서 오버헤드가 적지만, 데이터 손실이나 순서 변경이 발생할 수 있습니다. 온라인 게임, 영상 스트리밍, 센서 데이터 수집 같은 실시간성이 중요한 분야에서 주로 사용됩니다.

이 글에서는 TCP 소켓에 집중하며, 대부분의 기본 예제도 TCP 기반입니다.

TCP 소켓 통신의 동작 원리

TCP 기반 소켓 통신이 어떻게 이루어지는지 흐름을 이해하는 것이 중요합니다. 마치 두 사람이 전화통화를 하는 것처럼 생각하면 됩니다.

 

서버 측 동작

  1. 소켓 생성: ServerSocket 객체를 만들어 특정 포트에 바인딩합니다
  2. 리스닝: 클라이언트의 연결 요청을 기다립니다 (accept() 메서드 사용)
  3. 3-way handshake: 클라이언트와의 연결이 확립됩니다
  4. 데이터 송수신: InputStream과 OutputStream을 통해 데이터를 주고받습니다
  5. 연결 종료: 통신이 끝나면 소켓을 닫습니다

클라이언트 측 동작

  1. 소켓 생성: Socket 객체를 만들어 서버의 IP와 포트를 지정합니다
  2. 연결 요청: 서버에 연결을 시도합니다
  3. 3-way handshake: 서버와의 연결이 확립됩니다
  4. 데이터 송수신: InputStream과 OutputStream을 통해 데이터를 주고받습니다
  5. 연결 종료: 통신이 끝나면 소켓을 닫습니다

3-way handshake는 TCP에서 연결을 안전하게 확립하기 위한 과정인데, 정확히는

  • SYN: 클라이언트가 서버에 연결 요청을 보냅니다
  • SYN-ACK: 서버가 요청을 수락하고 응답을 보냅니다
  • ACK: 클라이언트가 서버의 응답을 확인합니다

이 과정이 완료되어야 실제 데이터 전송이 가능하며, 전체 흐름은 매우 중요하니 반드시 숙지하세요.

Java 소켓 클라이언트 구현하기

1단계: 간단한 Echo 클라이언트

먼저 서버로 메시지를 보내고 응답을 받는 가장 기본적인 클라이언트 예제입니다.

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

public class SimpleSocketClient {
    public static void main(String[] args) {
        String serverIP = "127.0.0.1";  // 서버 IP
        int serverPort = 9999;           // 서버 포트
        try {
            // 1. 서버에 연결
            Socket socket = new Socket(serverIP, serverPort);
            System.out.println("서버 연결됨: " + socket.getRemoteSocketAddress());
            // 2. 스트림 생성
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            // 3. 텍스트 전송용 래퍼
            PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream), true);
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 4. 메시지 보내기
            String message = "Hello from Client!";
            writer.println(message);
            System.out.println("보낸 메시지: " + message);
            // 5. 응답 받기
            String response = reader.readLine();
            System.out.println("서버 응답: " + response);
            // 6. 종료
            reader.close();
            writer.close();
            socket.close();
            System.out.println("연결 종료");
        } catch (IOException e) {
            System.err.println("통신 오류: " + e.getMessage());
        }
    }
}

이 예제를 실행하려면, 먼저 동작하는 서버가 필요합니다. 서버도 간단히 아래 참고용 코드를 참고하세요.

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleSocketServer {
    public static void main(String[] args) {
        int port = 9999;
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("서버 대기중: 포트 " + port);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("클라이언트 접속: " + clientSocket.getRemoteSocketAddress());
                InputStream inputStream = clientSocket.getInputStream();
                OutputStream outputStream = clientSocket.getOutputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream), true);
                String message = reader.readLine();
                System.out.println("받은 메시지: " + message);
                writer.println("Echo: " + message);
                reader.close();
                writer.close();
                clientSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2단계: 지속적인 통신 환경

한 번만 통신하는 것이 아니라, 여러 번 교류하는 상황을 위한 클라이언트입니다. 아래 참고하세요.

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class InteractiveSocketClient {
    private Socket socket;
    private PrintWriter writer;
    private BufferedReader reader;

    public InteractiveSocketClient(String serverIP, int serverPort) throws IOException {
        socket = new Socket(serverIP, serverPort);
        writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        String input;
        System.out.println("'exit'입력 시 종료됩니다.");
        while (true) {
            System.out.print("메시지 입력: ");
            input = scanner.nextLine();
            if ("exit".equalsIgnoreCase(input))
                break;
            writer.println(input);
            System.out.println("서버 답변: " + reader.readLine());
        }
        close();
    }

    private void close() throws IOException {
        reader.close();
        writer.close();
        socket.close();
        System.out.println("연결 종료");
    }

    public static void main(String[] args) {
        try {
            new InteractiveSocketClient("127.0.0.1", 9999).start();
        } catch (IOException e) {
            System.err.println("클라이언트 오류: " + e.getMessage());
        }
    }
}

HTTP와 소켓 차이점 정리

항목 소켓 통신 HTTP 통신
추상화 수준 낮음 (운영체제 제공, 개발자 직접 제어) 높음 (라이브러리/프레임워크 위에서 동작)
통신 방식 양방향, 지속 연결 요청-응답, 비지속적 (기본)
상태 유지 유지 가능 기본적으로 유지 안 함
메시지 형식 개발자 정의 HTTP 표준 기반
사용 사례 게임, 실시간 채팅, IoT 웹 서비스, API, 데이터 전달

 

Java 네트워크 프로그래밍은 기초 소켓부터 현대적 HTTP까지 이해하는 것이 매우 중요합니다. 상황에 맞는 도구 선택과 올바른 자원 관리 습관이 필요하며, 이를 토대로 안정적이고 효율적인 네트워크 서비스를 구축할 수 있습니다. 이번 글이 여러분의 네트워크 이해와 실습에 도움이 되길 바라며, 더 깊은 내용은 계속 공부하면서 익혀보세요.

 

 

자바 파일 및 디렉터리 완벽 가이드: Path와 Files로 배우는 실전 활용법

프로젝트를 진행할 때 파일을 생성하거나, 디렉터리 내 파일을 탐색하거나, 복사·삭제·이동하는 작업이 생각보다 자주 필요하다. 과거에는 File 클래스를 많이 썼지만, 자바 7에서 새로 도입된 N

byteandbit.tistory.com

 

반응형