소켓 통신의 종류

Jan 10, 2024
소켓 통신의 종류

기본 설정


notion image
notion image
notion image
notion image

소켓 통신의 흐름


  1. ServerSocket 객체 생성
  1. accept() 메서드 호출
  1. 소켓으로부터 Steam 객체 얻기
  1. 상호 대화
  1. 종료
 
실행 순서 : 서버 > 클라이언트

notion image

소켓 통신 과정


Server 입장

  1. Application데이터 생성
Application에서 데이터가 생성
이 데이터는 전송 계층으로 전달
  1. Segment 생성
전송 계층에서는 데이터를 Segment 로 나눔
각 Segment에는 TCP Header 와 Application데이터가 들어갑니다.
  1. Header 추가
각 Segment에는 TCP Header 가 추가
송신자와 수신자의 포트 번호, 시퀀스 넘버, 확인 응답 번호 등의 정보가 있음
  1. Packet생성
Segment에 TCP Header 가 추가
네트워크 계층으로 넘어가며 IP Header 가 추가 ⇒ Packet
  1. 데이터 전송
Packet은 네트워크를 통해 목적지로 전송
Packet의 전송 경로는 라우팅 등의 과정을 거쳐 목적지에 도착
  1. 도착지에서의 Packet처리
Packet이 도착지에 도달
네트워크 계층에서 IP Header를 제거하고 전송 계층으로 넘겨짐
  1. Segment 재조립
전송 계층에서는 Packet으로 도착한 데이터를 Segment로 다시 조립
  1. Header 제거
Segment에서는 TCP Header를 제거하고 Application 계층으로 데이터를 전달
  1. Application 데이터 처리
Application에서는 받은 데이터를 처리
 

Client 입장

  1. Application데이터 생성
Application에서 데이터가 생성
전송 계층으로 전달
  1. Segment생성
전송 계층에서는 데이터를 Segment로 나눔
각 Segment에는 TCP 헤더와 Application 데이터가 들어감
  1. Header추가
각 Segment에는 TCP Header가 추가
송신자와 수신자의 포트 번호, 시퀀스 넘버, 확인 응답 번호 등
  1. Packet생성
Segment에 TCP Header가 추가
네트워크 계층으로 넘어가며 IP Header가 추가 ⇒ Packet
  1. 데이터 전송
Packet은 네트워크를 통해 서버로 전송
Packet의 전송 경로는 라우팅 등의 과정을 거쳐 서버에 도착
  1. 도착지에서의 Packe처리
  1. Packet이 Server에 도착
네트워크 계층에서 IP Header를 제거하고 전송 계층으로 넘겨짐
  1. Segment 재조립
전송 계층에서는 Packet으로 도착한 데이터를 Segment로 다시 조립
  1. Header 제거
Segment에서는 TCP Header를 제거
Application 계층으로 데이터를 전달
  1. Application데이터 처리
Application에서는 받은 데이터를 처리

 

단방향 통신 : 한쪽만 보내는 것


package ex17.oneway; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { // 127.0.0.1 = localhost, 루프백 try { Socket socket = new Socket("127.0.0.1", 10000); Scanner sc = new Scanner(System.in); String msg = sc.nextLine(); BufferedWriter bw = new BufferedWriter( // 버퍼로 만듦 new OutputStreamWriter(socket.getOutputStream(), "UTF-8") // 보조스트림 만듦 -> 아웃풋 스트림 연결 ); bw.write(msg+"\n"); // 버퍼에 입력 bw.flush(); // 강제 푸쉬 } catch (IOException e) { System.out.println(e.getMessage()); e.printStackTrace(); // 에러 자세히 확인 } } }
package ex17.oneway; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(10000); // 소켓 생성 Socket socket = serverSocket.accept(); // 리스너 : 요청하는지 확인 System.out.println("클라이언트 연결됨"); BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8") ); String msg = br.readLine(); System.out.println(msg); } catch (IOException e) { throw new RuntimeException(e); } } }
notion image
notion image
 
flush를 안할 경우 전송되지 않음
notion image

  • main 스레드가 리스너 역할을 하고 받은 요청을 처리함
  • 소켓은 2개가 필요함
  • 보통 try/catch/finally 사용

반이중 통신 : Stateless


package ex17.half; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 20000); // 소켓 생성 // 버퍼 만들기(send) PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); pw.println("1"); // 영화 요청 // 버퍼 만들기(receive) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); String responseMsg = br.readLine(); System.out.println("서버로 부터 받은 메세지 : " + responseMsg); } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.half; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(20000); // 리스너 소켓 생성 Socket socket = serverSocket.accept(); // 연결후 랜덤포트 소켓 생성 //소켓 연결 완료됨 // 버퍼 만들기(recwived) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메세지 : " + requestMsg); // 프로토콜 적용하기 // 버퍼 만들기(send) -> 동기적 : 순서대로 실행 PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); // 내부가 버퍼드 라이터가 구현되어 있음 if(requestMsg.equals("1")) { pw.println("영화"); // 내부에 '\'가 내제되어 있음 - 응답 } else if(requestMsg.equals("2")) { pw.println("드라마"); } else { pw.println("프로토콜을 확인하세요 : 1은 영화, 2는 드라마"); } } catch (IOException e) { throw new RuntimeException(e); } } }
notion image

  • 한쪽이 보내면 응답을 하는 것
양방향 통신이 가능하지만 동시에 양쪽 방향에서 전송할 수 없음
요청에 대한 반응일 뿐 동시는 안됨
ex) 무전기
  • 버퍼가 총 4개가 달림
  • 요청과 응답 관계 → 트리거에 프로토콜이 작동해서 반응함
  • 단기적 / 스레드 1개로 됨
  • Web, Http 이 사용 → 선도 끊기고 요청자의 상태를 기억도 안 함
ex) 네이버에 주소를 검색했을 때
F5 - 재요청
💡
요청 시에만 응답하면 됨
 

전이중 통신 : Stateful


1) 내가 작성한 코드
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 10000); // 소켓 생성 //System.out.println("클라이언트 : 소켓 생성 완료"); Thread sendThread = new Thread(() -> { //System.out.println("클라이언트 : 보내기 스레드 생성 완료"); try { // 버퍼 만들기(send) PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); //System.out.println("클라이언트 : 쓰기 버퍼 생성 완료"); System.out.println("클라이언트 : 통신 연결 완료"); while (true) { Scanner sc = new Scanner(System.in); String sendMsg = sc.nextLine(); pw.println(sendMsg); //System.out.println("클라이언트 : 메세지 키보드로 받아서 보내기 완료"); Thread.sleep(500); if(sendMsg.equals("끝")) { System.out.println("서버 : 통신 종료"); break; } } } catch (Exception e) { e.printStackTrace(); } }); // receivedThread Thread receivedThread = new Thread(() -> { //System.out.println("클라이언트 : 읽기 스레드 생성 완료"); try { // 버퍼 만들기(received) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); //System.out.println("클라이언트 : 읽기 버퍼 생성 완료"); while (true) { String requestMsg = br.readLine(); System.out.println("서버로부터 받은 메세지 : " + requestMsg); //System.out.println("서버로부터 받은 메세지 읽기 완료"); if(requestMsg.equals("끝")) { System.out.println("서버 : 통신 종료"); break; } } } catch (Exception e) { e.printStackTrace(); } }); sendThread.start(); receivedThread.start(); } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class Server { public static void main(String[] args) { try { // main 스레드 ServerSocket serverSocket = new ServerSocket(10000); // 리스너 소켓 생성 Socket socket = serverSocket.accept(); // 대기중 -> 연결후 랜덤포트 소켓 생성 //System.out.println("서버 : 연결 완료"); // receivedThread Thread receivedThread = new Thread(() ->{ // 스택이 달라서 readLine()에서 오류 발생 //System.out.println("서버 : 읽기 스레드 생성 완료"); try{ // 버퍼 만들기(received) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); //System.out.println("서버 : 읽기 버퍼 생성 완료"); System.out.println("서버 : 통신 연결 완료"); while(true) { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메세지 : " + requestMsg); //System.out.println("클라이언트로부터 받은 메세지 읽기 완료"); if(requestMsg.equals("끝")) { System.out.println("서버 : 통신 종료"); break; } } } catch (Exception e) { e.printStackTrace(); } }); Thread sendThread = new Thread(() -> { //System.out.println("서버 : 쓰기 스레드 생성 완료"); try { // 버퍼 만들기(send) PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); //System.out.println("서버 : 쓰기 버퍼 생성 완료"); while (true) { Scanner sc = new Scanner(System.in); String sendMsg = sc.nextLine(); pw.println(sendMsg); //System.out.println("서버 : 메세지 키보드로 받아서 보내기 완료"); if(sendMsg.equals("끝")) { System.out.println("서버 : 통신 종료"); break; } } } catch (Exception e) { e.printStackTrace(); } }); receivedThread.start(); sendThread.start(); } catch (IOException e) { throw new RuntimeException(e); } } }
notion image
notion image
notion image
 
2) 강사님 코드
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { // 1. 소켓과 버퍼 만들기 Socket socket = new Socket("192.168.0.44", 10000); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); // 2. 메시지 전송 스레드 new Thread(() -> { while (true) { String keyboardMsg = sc.nextLine(); pw.println(keyboardMsg); } }).start(); // 3. 메시지 읽기 스레드 BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); new Thread(() -> { while (true) { try { String serverMsg = br.readLine(); System.out.println("서버로부터 받은 메시지 : " + serverMsg); } catch (Exception e) { e.printStackTrace(); } } }).start(); } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.Charset; import java.util.Scanner; public class Server { public static void main(String[] args) { try { // 1. 소켓과 버퍼 만들기 ServerSocket serverSocket = new ServerSocket(20000); Socket socket = serverSocket.accept(); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); // 2. 메시지 받기 스레드 new Thread(() -> { while (true) { try { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메시지 : " + requestMsg); } catch (Exception e) { e.printStackTrace(); } }}).start(); new Thread(() -> { while (true) { String keyboardMsg = sc.nextLine(); pw.println(keyboardMsg); } }).start(); } catch (IOException e) { throw new RuntimeException(e); } } }

  • 동시에 양방향 전송이 가능함
ex) 전화
  • 보내는 스레드, 읽는 스레드 2개가 필요함
읽는 스레드는 계속 돌아야 함
  • 채팅 → 선도 안 끊기고 요청자의 상태를 기억함
ex) 카카오톡 : 내가 보내기 전에 연락이 옴
  • 반드시 답장을 안 해도 됨 → 트리거가 없음
💡
계속 연결되어있어서 반이중 보다 부하가 심함
notion image
 
IP 확인 방법
notion image
 
PrintWriter → 자동 flush
notion image
 
UTF-8 자동 설정
notion image
 
자동 설정이 안되어있을 경우
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true, Charset.forName("UTF-8"));
 
.println : \가 내장되어 있음
notion image
 
Share article

vosw1