다수의 클라이언트에게 지속적으로 채팅 서비스를 제공
ServerSocket
의 accept()
사용socket
반환socket
을 할당ServerWorker serverWorker = new ServerWorker(socket);
start()
Thread serverWorkerThread = new Thread(serverWorker); serverWorkerThread.start();
서버로 자신의 메세지를 출력하고, Thread와 서버로부터 오는 다른 클라이언트들의 메세지를 입력한다.
Socket
생성setDaemon(true)
-> ChatClient Thread가 종료될 때 ReceiverWorker도 함께 종료되도록 설정Socket
을 이용해 다른 클라이언트들의 메세지를 입력받아 콘솔에 출력하는 작업을 지속적으로 한다.package step7.inst;
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.ArrayList;
import java.util.Collections;
import java.util.List;
public class ChatServer {
/*
* ServerSocket 생성
* loop
* accept()
* ServerWorker 생성
* list.add(ServerWorker)
* Thread 생성
* start()
*/
// client와 통신을 하는 객체(ServerWorker>socket)의 리스트를 관리
// 다수의 ServerWorker Thread에 의해 공유되어 사용되는 리스트이므로 thread-safe 하게 처리해본다(동기화 처리)
// private ArrayList<ServerWorker> list=new ArrayList<ServerWorker>();
private List<ServerWorker> list = Collections.synchronizedList(new ArrayList<ServerWorker>());
public void go() throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(5432);
System.out.println("**ChatServer 실행**");
while(true) {
Socket socket = serverSocket.accept();
ServerWorker serverWorker = new ServerWorker(socket);
// list에 추가
list.add(serverWorker);
Thread serverWorkerThread = new Thread(serverWorker);
serverWorkerThread.start();
}
} finally {
if(serverSocket != null)
serverSocket.close();
}
}
/*
* 각각의 ServerWorker가 클라이언트로부터 입력받은 메세지를
* 접속한 전체 클라이언트에게 출력하기 위한 메서드
* 아래 메서드를 개별 ServerWorker의 run 메서드에서 호출해서 사용한다
*/
public void sendMessage(String message) {
//for loop 를 이용해 list에 있는 모든 ServerWorker 의 pw.println();
for(int i=0; i<list.size(); i++) {
list.get(i).pw.println(message);
}
}
//nested class
public class ServerWorker implements Runnable{
private Socket socket;
private String clientIp;
private BufferedReader br;
private PrintWriter pw;
public ServerWorker(Socket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
try {
chatting();
} catch (IOException e) {
e.printStackTrace();
System.out.println(clientIp + " 통신 중 오류 발생 " + e.getMessage());
} finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
// list에서 현재 객체를 삭제한다
list.remove(this);
System.out.println(clientIp + "님이 나가셨습니다");
sendMessage(clientIp + "님이 나가셨습니다");
}
}
}
public void chatting() throws IOException {
clientIp = socket.getInetAddress().toString();
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(), true); // true: autoflush
System.out.println(clientIp + "님이 입장하셨습니다");
sendMessage(clientIp + "님이 입장하셨습니다"); // 접속한 모든 사용자에게 메세지를 전송한다
while(true) {
String message = br.readLine(); // 친구의 메세지를 입력받는다
if(message == null || message.trim().equalsIgnoreCase("exit")) {
break;
}
// 접속한 모든 사용자에게 메세지를 출력한다
sendMessage(clientIp + "님:" + message);
}
}
}
public static void main(String[] args) {
try {
new ChatServer().go();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package step7.inst;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import common.IP;
public class ChatClient {
private Socket socket;
private BufferedReader br; // 친구들의 메세지를 입력
private Scanner scanner; // 콘솔에서 입력
private PrintWriter pw; // 콘솔에서 입력받은 자신의 메세지를 서버로 출력
/*
* Socket 생성
* ReceiverWorker 생성 , Thread 생성 , setDaemon(true) , start()
* 콘솔로부터 입력받은 (Scanner) 메세지를 서버로 출력하는 기능을 구현
* ( exit 를 입력하면 종료 )
*/
public void go() throws UnknownHostException, IOException {
try {
socket = new Socket(IP.LOCAL, 5432);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
scanner = new Scanner(System.in);
pw = new PrintWriter(socket.getOutputStream(), true); // auto flush: buffer에 있는 데이터를 즉시 출력
// 서버에서 메세지를 입력받을 스레드를 생성하고 start 시킨다
ReceiverWorker worker = new ReceiverWorker();
Thread receiverThread = new Thread(worker); // 친구들의 메세지를 입력받은 스레드를 생성
receiverThread.setDaemon(true); // 자신을 실행시킨 thread가 종료되면 함께 종료 되도록 설정
receiverThread.start();
// 자신의 메세지를 서버로 출력하는 작업 정의
System.out.println("**ChatClient 실행**");
while(true) {
String message = scanner.nextLine(); // 콘솔 상에서 대기하다가 사용자가 메세지 입력 후 엔터 누르면 동작
pw.println(message);
if(message.trim().equalsIgnoreCase("exit")) {
break;
}
}
} finally {
if(scanner != null)
scanner.close();
if(socket != null)
socket.close();
System.out.println("**ChatClient 종료**");
}
}
//nested class
public class ReceiverWorker implements Runnable{
//서버에서 친구들의 메세지를 입력받아 콘솔에 출력한다
@Override
public void run() {
try {
receiveMessage();
} catch (IOException e) {
e.printStackTrace();
}
}
public void receiveMessage() throws IOException {
while(true) {
String message = br.readLine();
if(message == null)
break;
System.out.println(message);
}
}
}
public static void main(String[] args) {
try {
new ChatClient().go();
} catch (IOException e) {
e.printStackTrace();
}
}
}