
기본 설정




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

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


flush를 안할 경우 전송되지 않음

- 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);
}
}
}

- 한쪽이 보내면 응답을 하는 것
양방향 통신이 가능하지만 동시에 양쪽 방향에서 전송할 수 없음
요청에 대한 반응일 뿐 동시는 안됨
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);
}
}
}



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) 카카오톡 : 내가 보내기 전에 연락이 옴
- 반드시 답장을 안 해도 됨 → 트리거가 없음
계속 연결되어있어서 반이중 보다 부하가 심함

IP 확인 방법

PrintWriter → 자동 flush

UTF-8 자동 설정

자동 설정이 안되어있을 경우
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true, Charset.forName("UTF-8"));
.println : \가 내장되어 있음

Share article