자바

week10: 자바 네트워크

aerimi-code 2025. 11. 9. 23:18

네트워크 기초 개념: 

ip와 port를 이용한 통신

 

 

IP주소 : 장치를 식별하는 고유한 숫자 주소

포트: 하나의 IP주소 내에서 여러 프로그램을 구분하는 번호.

 

=> 네트워크 통신은 IP주소 + 포트 번호를 조합하여 어떤 컴퓨터의 어떤 프로그램으로 정보를 보낼지 지정한다.

예: 127.0.0.1:8080

 

URL클래스:

프로토콜: 통신 규칙과 데이터 전송 방식 (TCP, UDP, HTTP 등), 통신언어

 

TCP/UDP:

연결방식 연결 지향 (1:1 연결 필수) 비연결 지향(그냥 데이터 보내)
신뢰성 높음 (순서 보장, 누락 없음) 낮음 (순서/누락 가능)
속도 상대적으로 느림 빠름
오류처리 수신 확인, 재전송 있음 수신 확인 없음
사용 예 채팅, 파일 전송, 웹 온라인 게임, 스트리밍
Java 클래스 Socket, ServerSocket DatagramSocket

 


소켓 프로그래밍의 구조: 서버와 클라이언트

 

 

서버와 클라이언트의 역할:

서버 (ServerSocket)

  • 클라이언트의 연결 요청을 기다림
  • 고정된 IP와 포트 번호 사용
ServerSocket listener = new ServerSocket(50023);
Socket socket = listener.accept(); // 연결 대기

 

클라이언트 (Socket)

  • 서버에 연결을 요청
Socket socket = new Socket("192.168.0.1", 50023);

 

 

socket:

프로그램이 네트워크를 통해 데이터를 주고받을 수 있도록 OS가 제공하는 창구 (시작점!).

IP주소와 포트 번호가 합쳐진 형태로 특정 프로그램을 식별한다.

 

네트워크 통신: 소켓과 소켓과의 데이터 교환

 

자바는 소켓 통신이 가능한 라이브러리를 지원한다. 

 

자바에서의 통신프로그램 구조

 

소켓을 만들어 소켓끼리 연결하고 정보를 주고 받는다. 

 

서버는 두개의 소켓을 가지고 있다. 

이유: ServerSocket은 listener socket으로 연결 요청을 듣고 오직 기다리는 역할만 한다. (문지기, 전화 교환원)

Socket은 ServerSocket이 요청을 수락(accept)할때마다 생성한다. 

 

주요 메소드

ServerSocket
Socket

 

통신과정:

 

  • 서버: 서버 소켓 생성 → 연결 대기(listen)
SocketSocket serverSocket = new ServerSocket(50023);

 

  • 클라이언트: 서버에 연결 요청
Socket s = new Socket("192.164.45.61", 50023);
  • 서버: 연결 수락(accept) → 새 소켓 생성
Socket socket = serverSocket.accept();

 

  • 양쪽: 입출력 스트림 생성하여 데이터 송수신
Scanner input = new Scanner(socket.getInputStream());
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);

 

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

 


🌊 스트림: 데이터가 흐르는 통로 

 

네트워크에서는 통로(Socket)를 통해 받은 데이터를 우리가 보기 편한 형태(문자열, 숫자)로 읽고 쓰는 과정을 거쳐야한다.

이 과정에 스트림이 쓰인다. 

 

네트워크 통신에서는 InputStream과 OutputStream은 소켓을 통해 연결된 두 지점 사이의 데이터 흐름(통로)을 나타낸다.

 

클래스 역할 비유
socket.getInputStream() 데이터가 들어오는 통로 소켓 창구로 들어오는 물을 담는 파이프
socket.getOutputStream() 데이터가 나가는 통로 소켓 창구에서 나가는 물을 흘려보내는 파이프

주의: 이 기본 스트림들은 데이터를 바이트(byte) 단위로 처리한다. 

 

 

하지만 InputStream과 OutputStream은 바이트단위로 처리하기 때문에, 사람이 읽을 수 있는 언어로 변환해주는 보조 클래스가 필요하다. (Scanner, PrintWriter, BufferedReader, BufferedWriter)

 

이 클래스들을 데코레이터 패턴 처럼 기본 스트림에 덧붙여 기능을 확장하는 것이다.

 

 

데이터 입력(읽기) 계열: InputStream 처리

클래스 역할 인자 (socket.getInputStream()) 사용하는 이유
Scanner 문자열/숫자를 쉽게 읽음 InputStream **줄 단위(nextLine())**나 공백 단위로 데이터를 읽기 가장 편리합니다.
BufferedReader 문자를 효율적으로 읽음 InputStreamReader 대용량의 데이터를 **줄 단위(readLine())**로 읽을 때 성능이 좋고 효율적입니다.
InputStreamReader 바이트 → 문자 변환 InputStream 바이트 스트림문자 스트림으로 변환하는 중간 역할을 합니다.

 

코드 해석:

Scanner input = new Scanner(socket.getInputStream());
//or
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

 

1.

  • socket.getInputStream(): 소켓에서 바이트 단위로 들어오는 통로를 얻는다.
  • new Scanner(...): 그 바이트 통로를 Scanner에 넣어, 문자열 형태로 데이터를 쉽게 읽을 수 있게 만든다.

2.

  • socket.getInputStream(): 바이트 통로를 얻습니다.
  • new InputStreamReader(...): 바이트를 문자로 변환합니다.
  • new BufferedReader(...): 변환된 문자를 버퍼(Buffer)에 모아뒀다가 효율적으로 한 줄씩 읽습니다.

 

 

 

 

데이터 출력(쓰기) 계열: OutputStream 처리

클래스 역할 인자 (socket.getOutputStream()) 사용하는 이유
PrintWriter 데이터를 쉽게 출력 OutputStream 문자열을 출력하기 가장 편리하며, println()으로 자동 줄바꿈자동 플러시 설정이 가능합니다.
BufferedWriter 문자를 효율적으로 출력 OutputStreamWriter 대용량의 데이터를 출력할 때 성능이 좋고 효율적입니다.
OutputStreamWriter 문자 → 바이트 변환 OutputStream 문자 스트림을 네트워크가 사용하는 바이트 스트림으로 변환하는 중간 역할을 합니다.

 

코드해석: 

PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
//or
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

 

1.

  • socket.getOutputStream(): 소켓으로 바이트 단위로 나가는 통로를 얻는다.
  • new PrintWriter(...): 그 바스트 통로에 문자열을 쉽게 쓸 수 있게 함.
  • true: Auto Flush(자동 플러시) 설정. 데이터를 쓸 때마다 버퍼를 비워(flush) 바로 네트워크로 전송하라는 의미 (네트워크 통신에서는 필수적)

2.

  • socket.getOutputStream(): 바이트 통로를 얻습니다.
  • new OutputStreamWriter(...): 문자를 바이트로 변환
  • new BufferedWriter(...): 변환할 문자를 버퍼에 모아뒀다가 한 번에 효율적으로 출력
  1.  

 

📦 버퍼란?

데이터가 임시로 저장되는 작은 임시 메모리 공간. 

 

❓ 왜 버퍼에 모으나?  

컴퓨터가 데이터를 하나하나 처리하는 것 -> 매우 비효율적 '

예시: 네트워크나 하드디스크에 데이터를 보낼 때는 데이터를 보낼 때 마다 시스템에 요청을 해야한다. -> 시간 소요

(문자 1개당 요청 필요)

 

=> 버퍼에 다 모으고 전송! -> 성능 빨라짐

 

스트림 생성 예제

// 입출력 스트림 생성
Scanner input = new Scanner(socket.getInputStream());
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);

// 데이터 송수신
output.println("Hello Server!"); // 전송
String message = input.nextLine(); // 수신

 

양방향 통신

 

문제점: 

단순 반복문으로 읽고 쓰기를 하면 blocking 발생합니다. 한쪽이 입력을 기다리는 동안 다른 쪽은 대기 상태가 되어 실시간 통신이 불가능합니다.

 

해결: 멀티스레드

  • 읽기 스레드: 상대방 메시지 수신 담당
  • 쓰기 스레드: 내 메시지 전송 담당
  • 두 작업을 독립적으로 동시에 처리
// 여러 클라이언트 처리
while(true) {
    Socket socket = serverSocket.accept();
    new ServerThread(socket, listWriters).start(); // 각 클라이언트마다 스레드 생성
}

 

 

 

 

 

 

 

 

 

'자바' 카테고리의 다른 글

week14: CRUD  (0) 2025.11.29
week 12: DB & JDBC  (0) 2025.11.16
week9.java GUI_Swing, Event 실습  (0) 2025.11.02
week5: 자바 SOLID원칙  (0) 2025.10.03
week4: 자바 Thread -2  (0) 2025.10.01