사용자 콘솔 입력과 서버로부터 메시지를 받는 부분을 별도의 스레드로 분리해야한다.
한명이 이야기하면 전부 그 이야기를 들을 수 있어야한다.
하나의 클라이언트가 보낸 메시지를 서버가 받은 다음 모든 클라이언트에게 메시지를 다시 전송해야한다.
public class ReadHandler implements Runnable {
private final DataInputStream input;
private final Client client;
public boolean closed = false;
public ReadHandler(DataInputStream input, Client client) {
this.input = input;
this.client = client;
}
@Override
public void run() {
while (true) {
try {
String message = input.readUTF();
System.out.println(message);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
client.close();
}
}
}
public synchronized void close() {
if (closed) return;
closed = true;
}
}
public class WriteHandler implements Runnable {
private static final String DELIMITER = "|";
private final DataOutputStream output;
private final Client client;
private boolean closed = false;
public WriteHandler(DataOutputStream output, Client client) {
this.output = output;
this.client = client;
}
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
try {
String username = getUsername(scanner);
output.writeUTF("/join" + DELIMITER + username);
while (true) {
String message = scanner.nextLine(); // 블로킹
if (message.isEmpty()) continue;
if (message.equals("/exit")) {
output.writeUTF(message);
break;
}
// "/" 로 시작하면 명령어, 나머지는 일반 메시지
if (message.startsWith("/")) {
output.writeUTF(message);
} else {
output.writeUTF("/message" + DELIMITER + message);
}
}
} catch (IOException | NoSuchElementException e) {
throw new RuntimeException(e);
} finally {
client.close();
}
}
private static String getUsername(Scanner scanner) {
System.out.println("이름을 입력하세요.");
String username;
do {
username = scanner.nextLine();
} while (username.isBlank());
return username;
}
public synchronized void close() {
if (closed) return;
try {
System.in.close(); // Scanner 입력 중지 (사용자의 입력을 닫음)
} catch (Exception e) {
throw new RuntimeException(e);
}
closed = true;
}
}
public class Client {
private final String host;
private final int port;
private Socket socket;
private DataInputStream input;
private DataOutputStream output;
private ReadHandler readHandler;
private WriteHandler writeHandler;
private boolean closed = false;
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws IOException {
socket = new Socket(host, port);
input = new DataInputStream(socket.getInputStream());
output = new DataOutputStream(socket.getOutputStream());
readHandler = new ReadHandler(input, this);
writeHandler = new WriteHandler(output, this);
Thread readThread = new Thread(readHandler);
Thread writeThread = new Thread(writeHandler);
readThread.start();
writeThread.start();
}
public synchronized void close() {
if (closed) return;
readHandler.close();
writeHandler.close();
closeAll(socket, input, output);
closed = true;
}
}
public class ClientMain {
public static final int PORT = 12345;
public static void main(String[] args) throws IOException {
Client client = new Client("localhost", PORT);
client.start();
}
}