

Vector) 해두고, 받은 메시지를 반복문으로 모든 클라이언트에게 보내면 (send())public class GUIServer extends JFrame {
...
Thread thread; // 서버 가동용 스레드 (메인스레드가 accept() 대기상태에 빠지지 않도록 분리 실행)
Vector<ServerChatThread> vec = new Vector<>(); // ✅ 접속 중인 클라이언트들의 스레드를 저장 (현재는 0)
...
public void startServer() {
int port = Integer.parseInt(t_port.getText());
try {
ServerSocket server = new ServerSocket(port);
area.append("서버 생성 및 접속자 감지 시작\n");
while (true) {
Socket socket = server.accept(); // 클라이언트가 접속할 때까지 블로킹 대기
String ip = socket.getInetAddress().getHostAddress();
area.append(ip + "님 접속 감지\n");
// 클라이언트와의 통신을 전담할 스레드 생성
ServerChatThread chatThread = new ServerChatThread(this, socket);
chatThread.start(); // 별도 스레드로 실행
// ✅ 현재 접속한 클라이언트 스레드를 목록(Vector)에 추가
vec.add(chatThread);
area.append("현재 " + vec.size() + "명 접속\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ArrayList도 가능은 하지만, 다중 스레드 환경에서는 동기화를 지원하지 않기 때문에 여러 스레드가 동시에 같은 인덱스를 조작하면 충돌 위험이 있음
➡ 개발자가 직접 synchronized {} 블럭으로 감싸서 동기화 코드를 작성해야 함
Vector는 내부적으로 동기화(synchronized)가 적용된 클래스이므로
별도 동기화 없이 멀티스레드 환경에서도 안전하게 사용할 수 있음
// 서버에 접속한 모든 클라이언트에게 메시지 전달 (브로드캐스트)
for (int i = 0; i < guiserver.vec.size(); i++) {
ServerChatThread st = guiserver.vec.get(i);
st.send(msg); // 각 클라이언트에게 전송
}
서버에 접속한 모든 유저와 1:1 대응하는 ServerChatThread수 만큼 반복하면서 메시지를 보내자!
클라이언트 측에서 서버 메시지를 실시간으로 수신하기 위한 스레드 클래스
listen : 사용자가 메시지를 보내지 않아도 서버의 메시지를 계속 청취해야 하므로 무한루프 필요send() : 사용자가 직접 입력할 때만 호출됨public class ClientChatThread extends Thread {
Client client; // GUI 요소를 제어하기 위한 참조 (ex: JTextArea 등)
Socket socket;
BufferedReader buffr; // 서버로부터 수신
BufferedWriter buffw; // 서버로 메시지 전송
public ClientChatThread(Client client, Socket socket) {
this.client = client;
this.socket = socket;
try {
// 소켓에서 입력/출력 스트림을 뽑아서 버퍼 연결
buffr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
buffw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
// 사용자가 입력한 메시지를 서버로 전송 (사용자가 보내기 원할 때만)
public void send(String msg) {
try {
buffw.write(msg + "\n");
buffw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// 서버에서 보낸 메시지를 실시간으로 수신하여 GUI에 출력
public void listen() {
String msg = null;
try {
msg = buffr.readLine(); // 서버가 보낸 메시지 수신
client.area.append(msg + "\n"); // GUI에 출력
} catch (IOException e) {
e.printStackTrace();
}
}
// ✅ 스레드가 시작되면 무한 루프로 listen() 실행
@Override
public void run() {
while (true) {
listen();
}
}
}
// 서버에 접속하는 메서드: IP와 포트번호로 소켓을 생성한다
public void connectServer() {
String ip = (String) cb_ip.getSelectedItem(); // 콤보박스에서 선택된 서버 IP
int port = Integer.parseInt(t_port.getText()); // 입력된 포트 번호
try {
// 서버에 연결 시도 → 성공하면 서버와의 통신을 위한 소켓 생성됨
Socket socket = new Socket(ip, port);
// ✅ 접속 이후부터는 메시지 수신을 별도 스레드가 담당해야 하므로,
// 소켓을 ClientChatThread에 넘겨주고 스레드 실행
clientThread = new ClientChatThread(this, socket);
clientThread.start(); // 내부 run()에서 listen() 무한 실행됨
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
접속 이후부터 채팅은 스레드가 담당하므로, 소켓을 스레드에 전달해주자!
// 텍스트 입력창(t_input)에 엔터 키 이벤트 등록
t_input.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 사용자가 엔터를 누르면 현재 입력값을 서버로 전송
clientThread.send(t_input.getText());
t_input.setText("");
}
}
});
clientThread.start()는 listen()을 무한 루프로 돌려서 서버 메시지를 계속 수신send()는 엔터를 눌렀을 때만 1회 호출되어 입력값을 서버로 전송누가 채팅방을 나가면
ServerChatThread의 readLine()부분이 에러가 남
➡ flag방식으로 해결하기
// 클래스 멤버에 추가
private boolean isRunning = true;
@Override
public void run() {
while (isRunning) { // 내부에서 isRunning=false가 되면 루프 종료됨
listen();
}
}
// 수신
public void listen() {
String msg = null;
try {
msg = buffr.readLine(); // 클라이언트가 보낸 메시지 수신
if (msg == null) throw new IOException("클라이언트 연결 끊김");
guiserver.area.append(msg + "\n");
// 전체 클라이언트에게 메시지 전송
for (int i = 0; i < guiserver.vec.size(); i++) {
ServerChatThread st = guiserver.vec.get(i);
st.send(msg);
}
} catch (IOException e) {
// ⛔ 예외 발생 시 실행
isRunning = false; // ✅ 루프 종료 유도
guiserver.vec.remove(this); // ✅ 나(서버 스레드)는 해당 클라이언트 전용이므로,
// 연결 종료 시 접속자 목록에서 제거
guiserver.area.append("현재 접속자 " + guiserver.vec.size() + " 명\n");
System.out.println("어! 누구 나갔다");
}
}