[JAVA] 69. 소켓 통신

문정준's avatar
Feb 20, 2025
[JAVA] 69. 소켓 통신
다른 디바이스와 통신을 하기 위해서 필요한 것
  1. 프로토콜
  1. IP 주소 (4바이트 : 42.9억개)
  1. 포트 번호 (2바이트 : 65000개)
  1. 소켓 + 버퍼(BR, BW)
 

1. 통신을 하기 위해 필요한 것

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

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

3. 고정 IP / 내부 IP

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

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); } } }
주의할 점
  1. 서버 측 코드를 우선 실행하고 클라이언트를 실행해야 통신이 정상적으로 작동
  1. localhost : loopback address
      • 나의 주소를 라우터에서 받아오는 게 아닌, 바로 자신을 가리키게 하는 키워드
  1. 항상 입출력은 App의 입장에서 생각하기
      • 들어오면 Input, 나가면 Output
      • Output 시 ‘\n’ 필수 포함! ( 없으면 null로 인식 )
  • Client
notion image
  • Server
notion image

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); } } }
반이중 통신 방식 구조
  1. 클라이언트 측의 데이터 요청 (BufferedReader를 이용한 키보드 입력)
  1. 서버 측의 BufferedReader를 통한 데이터 확인 후 파싱
      • 프로토콜에 따른 데이터 해석 및 응답 준비
  1. 서버 측의 BufferedWriter와 프로토콜을 통한 응답
  1. 클라이언트 측의 BufferedReader를 통한 응답 확인
  1. 1 ~ 4 반복 혹은 소켓 close (세션 종료)
  • Client
notion image
  • Server
notion image

6. 전이중 통신

  • Full-duplex 방식, 동시에 데이터 요청, 응답이 이루어짐
  • 스레드(thread)를 활용
notion image
 
 

🚫 통신이 안되는 이유 : 방화벽 (Firewall)

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

sxias