while (true) {
String msg = reader.readLine(); // 상대방 메시지 읽기 → 여기서 멈춤
writer.println("내 답장"); // 내 메시지 보내기
}
//문제점
읽는 동안에는 쓸 수 없고
쓰는 동안에는 읽을 수 없다.
상대방이 말해야만 내가 말할 수 있는 구조
→ 진짜 채팅처럼 동시에 주고받는 것이 불가능
//해결책
해결책은 읽기와 쓰기를 별도 스레드로 분리하는 것
-서버 측 코드
package server.ch03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class WhileServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(5000)) {
System.out.println("클라이언트에 연결 요청을 기다립니다...");
Socket clientSocket = serverSocket.accept();
System.out.println("==========서버 실행===========");
//소켓과 연결 된 스트림
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(),true);
//키보드와 연결할 스트림
BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = reader.readLine()) != null) {
//클 --->exit <--- EXIT
//equalsIgnoreCase 대소문자 구분 없이 값 확인
if ("exit".equalsIgnoreCase(line)) {
break; // while 종료
}
System.out.println("클라이언트 > " + line);
//서버 측 컴퓨터의 키보드에서 값을 받아서 클라이언트 측으로 전송
System.out.print("서버입력 > ");
//**서버에서 응답을 받아야만 메세지를 보낼 수 있음 **
String severMsg = keyboardReader.readLine(); //블로킹 상태(콘솔 창에 글 입력해야 함)
//클라이언트와 연결 된 소켓 출력 스트림을 활용해서 내용을 전달한다.
writer.println(severMsg);
//서버측에서도 더이상 글 입력 받기 싫다면 exit입력으로 while문 종료 처리
if ("exit".equalsIgnoreCase(severMsg)) {
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package client.ch03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class WhileClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 5000)) {
//소켓에서 연결 할 입력, 출력 스트림 2개가 필요하다.
//클라이언트에서 키보드에서 값을 입력 받을 스트림이 필요하다.
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//클라이언트에서 키보드에서 값을 입력 받을 스트림이 필요하다
BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));
String line;
while (true) {
System.out.print("클라이언트 입력 > ");
String input = keyboardReader.readLine(); //블로킹 상태
writer.println(input);
if ("exit".equalsIgnoreCase(input)) {
break;
}
//서버측에서 보낸 메세지를 받아서 클라이언트 콘솔창에 출력
String response = reader.readLine();
if ("exit".equalsIgnoreCase(response)) {
System.out.println("서버측에서 대화 종료 요청");
break;
}
System.out.println("서버 응답 : " + response);
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
메인 스레드
├── 읽기 스레드: 상대방 메시지를 계속 기다리며 읽기
└── 쓰기 스레드: 내 키보드 입력을 계속 기다리며 전송
두 스레드가 동시에 실행 → 읽으면서 동시에 쓸 수 있음
-서버 측 코드
package server.ch03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiThreadedServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(5000)) {
System.out.println("클라이언트에 연결 요청을 기다립니다...");
Socket clientSocket = serverSocket.accept();
System.out.println("==========서버 실행===========");
//소켓과 연결 된 스트림
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
//키보드와 연결할 스트림
BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));
//읽기 스레드 : 클라이언트 메세지를 계속 수신
Thread readThread = new Thread(new Runnable() {
@Override
public void run() {
try {
String clientMessage;
while ((clientMessage = reader.readLine()) != null) {
if ("exit".equalsIgnoreCase(clientMessage)) {
System.out.println("클라이언트 종료했습니다");
break;
}
System.out.println("클라이언트 메세지 : " + clientMessage);
}
} catch (IOException e) {
System.err.println("클라이언트와 연결이 끊겼습니다");
}
}
});
//쓰기 스레드 : 키보드 입력을 받아 클라이언트에게 전송
Thread writeThread = new Thread(new Runnable() {
@Override
public void run() {
try {
String serverMessage;
while ((serverMessage = keyboardReader.readLine()) != null) {
writer.println(">>> " + serverMessage);//"\n" 포함
if ("exit".equalsIgnoreCase(serverMessage)) {
System.out.println("서버가 종료되었습니다");
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
readThread.start(); //읽기 스레드 시작
writeThread.start();// 쓰기 스레드 시작
readThread.join(); // 읽기 스레드가 종료까지 대기
writeThread.join(); // 쓰기 스레드가 종료까지 대기
/**
* join() = 이 스레드가 끝날 때 까지 기다려 줘 라는 의미
* Thread.sleep() 이 "N초동안 잠깐 잠들어 멈춰" 라면
* join()은 저 스레드가 끝날 때 까지 멈춰이다
*
* join() 이 없으면
* main 스레드 바로 try 블록을 벗어남
* 소켓 자동 close()
* 아직 실행 중인readThread, writeThread 가 닫힌 소캣으로 통신시도 ....오류발생
*/
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
-클라이언트 측 코드
package client.ch03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class MultiThreadedClient {
public static void main(String[] args) {
//다른 사람 컴퓨터 IP넣으면 서로 응답 가능
try (Socket socket = new Socket("localhost", 5000)) {
//소켓에서 연결 할 입력, 출력 스트림 2개가 필요하다.
//클라이언트에서 키보드에서 값을 입력 받을 스트림이 필요하다.
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//클라이언트에서 키보드에서 값을 입력 받을 스트림이 필요하다
BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));
//읽기 쓰레드 (서버 측에서 값을 계속 받을 수 있도록 처리)
Thread readThread = new Thread(new Runnable() {
@Override
public void run() {
try {
String serverMessage;
while ((serverMessage = reader.readLine()) != null) {
if ("exit".equalsIgnoreCase(serverMessage)) {
System.out.println("서버가 종료했습니다");
}
System.out.println("서버 : " + serverMessage);
}
} catch (IOException e) {
System.out.println("서버측과 연결이 끊어졌습니다");
}
}
});
//스레드 쓰기 생성(클라이언트 측 키보드에서 값을 받아서 서버측으로 전송)
Thread writeThread = new Thread(new Runnable() {
@Override
public void run() {
try {
String clientMessage;
while ((clientMessage = keyboardReader.readLine()) != null) {
writer.println(">>> " + clientMessage);
if ("exit".equalsIgnoreCase(clientMessage)) {
System.out.println("클라이언트가 종료했습니다");
break;
}
}
} catch (IOException e) {
System.out.println("메세지 전송 중 오류가 발생했습니다");
}
}
});
readThread.start();
writeThread.start();
readThread.join();
writeThread.join();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}