네트워크 기초 개념:


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)할때마다 생성한다.
주요 메소드


통신과정:

- 서버: 서버 소켓 생성 → 연결 대기(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개당 요청 필요)
=> 버퍼에 다 모으고 전송! -> 성능 빨라짐
스트림 생성 예제
// 입출력 스트림 생성
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 |