다른 디바이스와 통신을 하기 위해서 필요한 것
- 프로토콜
- IP 주소 (4바이트 : 42.9억개)
- 포트 번호 (2바이트 : 65000개)
- 소켓 + 버퍼(BR, BW)
1. 통신을 하기 위해 필요한 것
- 각 앱(프로그램)은 실행되면 프로세스로 전환
- 프로세스들은 고유 번호인 PID (Process ID) 를 가짐
- PID는 CPU가 관리
- 외부에서 데이터가 필요한 (통신을 하는) 프로세스들은 포트 번호를 가짐
- 포트 번호는 중복될 수 없음 (중복 시 데이터 충돌)
- 통신을 하기 위해 필요한 것
- 프로토콜 : 통신 방법, 규약
- IP 주소 : 통신을 할 상대의 주소, 약 42.9억개
- 포트 번호 : 데이터를 받거나 보낼 프로세스의 번호, 약 65000개
- 소켓 : 데이터가 들어오고 나가는 공간
- 버퍼를 활용 (BufferedWriter, BufferedReader)

2. 소켓 통신
- 소켓을 통한 통신을 위해서는 3가지 정보로 소켓을 만들어야 함
- 프로토콜 - TCP, UDP
- IP 주소 : Client 측에서 Server에게 접속
- 포트 번호 : 어떤 프로세스에 접근할 것인지 확인
- 통신을 진행할 때 전송되는 데이터는 Header와 Body로 구분
- Header : IP 주소, 포트 번호 등이 달려있는 세그먼트 ( Redundancy, Overhead )
- Body : 본 내용 ( Plain Text or Payload )
- 어플리케이션 층에서도 프로토콜이 필요
- 가장 유명한 것이 HTTP 프로토콜 ( URL을 받아 서버에서 파일 전송 )
- 기다리면 Server, 요청하면 Client
- Client가 요청 > Server가 소켓 연결 후 데이터 전송 (응답) > Client가 응답

- 서버가 소켓을 Open하면, 소켓 내부의 Listener는 포트에 접속하는 다른 기기를 관찰
- 포트로 접속한 것을 확인하면, 다른 기기와 소켓을 연결
- 이때, 서버의 포트가 아닌 무작위의 포트를 선정하여 소켓을 연결함
- 포트를 연결하는 동안 서버 소켓은 다시 요청이 들어오는 기기를 관찰
- Client - Server 간 통신 방법
- 클라이언트 측의 데이터 요청 (BufferedReader를 이용한 키보드 입력)
- 서버 측의 BufferedReader를 통한 데이터 확인 후 파싱
- 프로토콜에 따른 데이터 해석 및 응답 준비
- 서버 측의 BufferedWriter와 프로토콜을 통한 응답
- 클라이언트 측의 BufferedReader를 통한 응답 확인

3. 고정 IP / 내부 IP
- 인터넷을 사용할 때, 인터넷의 종류에 따라 고정 IP / 유동 IP가 나뉨
- 유동 IP의 경우 가상의 IP를 부여받고, 이는 실제 IP와는 차이가 있음
- 공유기의 ‘192.168.0.1’ 주소가 보통 가상 IP 주소

4. 단방향 통신
- Client - BufferedWriter
package ex20.ch01;
import java.io.*;
import java.net.Socket;
public class MyClient01 {
public static void main(String[] args) {
// 단방향 (One-way) 통신 : 클라이언트
// localhost : loopback address (내 주소)
try {
// 소켓 연결
Socket socket = new Socket("localhost", 20000);
// 키보드 입력
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("키보드 입력 대기중..");
String msg = in.readLine();
// 전송
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
out.write(msg);
out.write("\n");
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- Server - BufferedReader
package ex20.ch01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer01 {
public static void main(String[] args) {
// 단방향 (One-way) 통신 : 서버
try {
ServerSocket ss = new ServerSocket(20000);
System.out.println("서버 소켓이 대기중입니다. 연결을 시도해주세요.");
// 서버 소켓이 연결을 인지하면 새 소켓 생성 (포트는 무작위)
Socket socket = ss.accept(); // 프로세스 멈춤 (대기 상태)
System.out.println("소켓이 연결되었습니다.");
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String body = "";
body = br.readLine();
System.out.println("서버측 : " + body);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
주의할 점
- 서버 측 코드를 우선 실행하고 클라이언트를 실행해야 통신이 정상적으로 작동
- localhost : loopback address
- 나의 주소를 라우터에서 받아오는 게 아닌, 바로 자신을 가리키게 하는 키워드
- 항상 입출력은 App의 입장에서 생각하기
- 들어오면 Input, 나가면 Output
- Output 시 ‘\n’ 필수 포함! ( 없으면 null로 인식 )
- Client

- Server

5. 반이중 통신
- Half-duplex 방식, 데이터를 전송받고 그에 대한 응답을 다시 전송
package ex20.ch02;
import java.io.*;
import java.net.Socket;
public class MyClient02 {
public static void main(String[] args) {
// 반이중 (Half-duplex) 통신 : 클라이언트
// localhost : loopback address (내 주소)
try {
// 소켓 연결
Socket socket = new Socket("localhost", 20000);
// 키보드 입력
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("키보드 입력 대기중..");
String reqbody = in.readLine();
// 전송
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
out.write(reqbody);
out.write("\n");
out.flush();
// 응답 확인
BufferedReader in2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String respbody = in2.readLine();
System.out.println(respbody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package ex20.ch02;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer02 {
public static void main(String[] args) {
// 반이중 (Half-duplex) 통신 : 서버
try {
ServerSocket ss = new ServerSocket(20000);
System.out.println("서버 소켓이 대기중입니다. 연결을 시도해주세요.");
// 서버 소켓이 연결을 인지하면 새 소켓 생성 (포트는 무작위)
Socket socket = ss.accept(); // 프로세스 멈춤 (대기 상태)
System.out.println("소켓이 연결되었습니다.");
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String reqbody = "";
reqbody = br.readLine();
System.out.println("서버측 : " + reqbody);
// 전송 받은 후 응답
// 프로토콜 작성 ( name을 받으면 metacoding, age를 받으면 39 전송 )
String respbody = "";
if (reqbody.equals("name")) {
respbody = "metacoding";
} else if (reqbody.equals("age")) {
respbody = "39";
} else {
respbody = "error";
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(respbody);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
반이중 통신 방식 구조
- 클라이언트 측의 데이터 요청 (BufferedReader를 이용한 키보드 입력)
- 서버 측의 BufferedReader를 통한 데이터 확인 후 파싱
- 프로토콜에 따른 데이터 해석 및 응답 준비
- 서버 측의 BufferedWriter와 프로토콜을 통한 응답
- 클라이언트 측의 BufferedReader를 통한 응답 확인
- 1 ~ 4 반복 혹은 소켓 close (세션 종료)
- Client

- Server

6. 전이중 통신
- Full-duplex 방식, 동시에 데이터 요청, 응답이 이루어짐
- 스레드(thread)를 활용

🚫 통신이 안되는 이유 : 방화벽 (Firewall)
- 기본적으로 방화벽은 모든 포트를 차단 ( Negative Strategy )
- 어플리케이션이 포트 통신을 요청하면 그 포트 번호를 허용 ( Inbound )
- Inbound를 통해 내/외부 네트워크에 Access 가능한 서버를 DMZ라고 함
- DMZ 개방 : 방화벽의 모든 포트를 개방하여 어디서든 DMZ 서버에 접근 가능한 상태

Share article