Java 네트워크 프로그래밍 핵심 정리

네트워크 프로그래밍은 웹 개발과 다릅니다. 웹 사이트를 만드는 것이 아니라, 서로 다른 위치에 있는 컴퓨터들이 데이터를 주고받을 수 있도록 연결하는 기술입니다. 통신 규약(프로토콜)을 통해 데이터 전송 규칙을 정하고, 하드웨어와 소프트웨어 인터페이스를 구현하여 노드 간 정보 교환을 가능하게 합니다.

OSI 계층 모델과 프로토콜 비교

특성TCPUDP
연결 방식연결 지향비연결
신뢰성높음 (순서 보장, 재전송)낮음 (순서 미보장)
속도상대적으로 느림빠름
용도파일 전송, 이메일실시간 스트리밍, 게임

TCP 소켓 통신 구현

다음은 기본적인 에코 서버-클라이언트 예제입니다. 서버는 클라이언트의 접속을 대기하며, 연결 후 메시지를 수신합니다.

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

public class TcpServerBasic {
    public static void main(String[] args) {
        try (ServerSocket listener = new ServerSocket(7777)) {
            System.out.println("서버 대기 중...");
            
            Socket client = listener.accept();
            System.out.println("클라이언트 연결됨: " + client.getInetAddress());
            
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(client.getInputStream())
            );
            
            String received = reader.readLine();
            System.out.println("수신: " + received);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.*;

public class TcpClientBasic {
    public static void main(String[] args) {
        try (Socket channel = new Socket("localhost", 7777)) {
            
            PrintWriter sender = new PrintWriter(
                channel.getOutputStream(), true
            );
            sender.println("안녕하세요, 서버!");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

양방향 TCP 통신 예제

실무에서는 요청-응답 패턴이 일반적입니다. 서버가 클라이언트에게 응답을 보내는 방식으로 구현합니다.

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

public class EchoService {
    public static void main(String[] args) {
        try (ServerSocket gateway = new ServerSocket(6666)) {
            
            while (true) {
                Socket peer = gateway.accept();
                handleConnection(peer);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void handleConnection(Socket peer) {
        try (peer;
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(peer.getInputStream()));
             PrintWriter out = new PrintWriter(
                 peer.getOutputStream(), true)) {
            
            String payload = in.readLine();
            System.out.println("클라이언트 메시지: " + payload);
            
            out.println("ECHO: " + payload);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.*;

public class EchoRequester {
    public static void main(String[] args) {
        try (Socket pipe = new Socket("127.0.0.1", 6666);
             BufferedReader responseReader = new BufferedReader(
                 new InputStreamReader(pipe.getInputStream()));
             PrintWriter requestWriter = new PrintWriter(
                 pipe.getOutputStream(), true)) {
            
            requestWriter.println("반갑습니다");
            
            String reply = responseReader.readLine();
            System.out.println("서버 응답: " + reply);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UDP 데이터그램 통신

UDP는 연결 설정 없이 즉시 데이터를 전송합니다. DatagramPacket과 DatagramSocket을 사용하며, 각 패킷은 독립적으로 목적지로 전달됩니다.

import java.net.*;

public class UdpReceiver {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket(5555)) {
            
            byte[] bucket = new byte[2048];
            DatagramPacket packet = new DatagramPacket(bucket, bucket.length);
            
            while (true) {
                socket.receive(packet);
                
                String text = new String(packet.getData(), 0, 
                    packet.getLength(), "UTF-8");
                System.out.println("[" + packet.getAddress() + "] " + text);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import java.net.*;

public class UdpSender {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket()) {
            
            String message = "UDP 메시지 전송";
            byte[] payload = message.getBytes("UTF-8");
            
            DatagramPacket packet = new DatagramPacket(
                payload,
                payload.length,
                InetAddress.getByName("127.0.0.1"),
                5555
            );
            
            socket.send(packet);
            System.out.println("전송 완료");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

UDP 바이너리 데이터 전송

원시 타입이나 객체를 전송할 때는 ByteArray 스트림을 활용하여 바이트 배열로 변환합니다.

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

public class BinaryUdpServer {
    public static void main(String[] args) {
        try (DatagramSocket endpoint = new DatagramSocket(4444)) {
            
            byte[] cache = new byte[1024];
            DatagramPacket envelope = new DatagramPacket(cache, cache.length);
            
            endpoint.receive(envelope);
            
            ByteArrayInputStream raw = new ByteArrayInputStream(
                envelope.getData(), 0, envelope.getLength());
            DataInputStream decoder = new DataInputStream(raw);
            
            double value = decoder.readDouble();
            boolean flag = decoder.readBoolean();
            
            System.out.printf("받은 데이터: %.2f, %b%n", value, flag);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.*;

public class BinaryUdpClient {
    public static void main(String[] args) {
        try (DatagramSocket endpoint = new DatagramSocket()) {
            
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            DataOutputStream encoder = new DataOutputStream(buffer);
            
            encoder.writeDouble(3.14159);
            encoder.writeBoolean(true);
            encoder.flush();
            
            byte[] payload = buffer.toByteArray();
            
            DatagramEnvelope envelope = new DatagramPacket(
                payload,
                payload.length,
                InetAddress.getByName("127.0.0.1"),
                4444
            );
            
            endpoint.send(envelope);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

포트 번호 선택 가이드

  • 0 ~ 1023: 알려진 포트(HTTP 80, HTTPS 443 등), 사용 금지
  • 1024 ~ 49151: 등록된 포트, 충돌 주의
  • 49152 ~ 65535: 동적/사설 포트, 사용자 정의에 적합
  • TCP와 UDP는 독립적인 포트 공간을 가짐(같은 번호 사용 가능)

핵심 클래스 정리

TCPUDP
ServerSocketDatagramSocket
SocketDatagramPacket
InputStream/OutputStreamByteArray 스트림(직접 구성)

태그: java tcp UDP socket DatagramSocket

5월 31일 10:42에 게시됨