Toy Project

[프로젝트] - 멀티 스레드를 활용한 채팅 프로그램 (Java)

Jesse 2021. 4. 4. 01:22

오늘은 자바를 공부하면서 소규모 프로젝트로 멀티 스레드를 활용한 다중 클라이언트 채팅 프로그램을 만들어 보았습니다.

채팅 프로그램의 작업 흐름

  1. 서버 쪽에서 SeverSocket을 생성
  2. 클라이언트 쪽에서 해당 IP에 Socket을 생성
  3. 서버쪽에서 접속하려는 클라이언트를 accept()하고 리스트에 클라이언트 저장
  4. 클라이언트가 작업 요청 (채팅 프로그램이니 작업은 메세지 전송)
  5. 서버 쪽 스레드에서 요청을 처리
  6. 응답을 클라이언트에게 반환
    1. 받은 메세지를 리스트에 있는 모든 클라이언트들에게 전달
    2. 이유는 단체 채팅방에 있는 모든 유저는 채팅을 보낸 유저의 채팅을 볼수 있어야 하기 때문

 

package step6;

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;

/*
 Chatting 서버
 1. 클라이언트의 접속 -> ServerWorker Thread 생성 및 start
 2. ServerWorker -> 개별 client에 채팅 서비스
 	
 */
public class ChatServer {
	private ArrayList<ServerWorker> list = new ArrayList<ServerWorker>();

	public void go() throws IOException {
		ServerSocket serverSocket = null;
		try {

			// 채팅 서버 시작
			serverSocket = new ServerSocket(5432);
			System.out.println("**step6.ChatServer**");

			// 다수의 클라이언트에게 지속적으로 서비스하기 위해 while 이용
			while (true) {
				Socket socket = serverSocket.accept();
				ServerWorker sw = new ServerWorker(socket);
				list.add(sw);
				Thread thread = new Thread(sw);
				thread.start();
			}

		} finally {
			if (serverSocket != null)
				serverSocket.close();
			System.out.println("**ChatServer 종료합니다**");
		}
	}
	
	public void sendMessage(String message) {
		System.out.println(message);
        // 접속해 있는 모든 클라이언트들에게 메세지 전송
		for (int i=0;i<list.size();i++) {
			list.get(i).pw.println(message);
		}
	}
	
	class ServerWorker implements Runnable {
		private Socket socket;
		private BufferedReader br;
		private PrintWriter pw;
		private String user;

		public ServerWorker(Socket socket) {
			super();
			this.socket = socket;
			user = socket.getInetAddress().toString();
		}
		public void chatting() throws IOException {
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(socket.getOutputStream(),true);
			sendMessage(user+"님이 입장하셨습니다");

			try {
				while (true) {
					String message = br.readLine();
					if (message.trim().equals("종료") || message.equals("null") || message == null) {

						break;
					}
					sendMessage(user+"님:"+message);
					
				} // while
			} // echo method
			catch (Exception e) {
			}
		}

		public void run() {
			try {
				chatting();

			} catch (IOException e) {
				e.printStackTrace();
			}finally {
				try {
					closeAll();
				} catch (IOException e) {
					e.printStackTrace();
				}
				sendMessage(user+"님이 나가셨습니다!!");
				list.remove(this);
			}
		}

		public void closeAll() throws IOException {
			if (pw != null)
				pw.close();
			if (br != null)
				br.close();
			if (socket != null)
				socket.close();
		}

	}

	public static void main(String[] args) {
		try {
			new ChatServer().go();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

 

package step6;

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 step1.IP;

/*
채팅 클라이언트
스레드 ( 세부적 실행단위 )
1. ReceiverWorker implements Runnable : 친구들의 메세지를 입력받는 역할
2. ChatClient의 main thread : ReceiverWorker Thread 생성 start
	자신은 친구들에게 메세지를 출력하는 역할	
 */
public class ChatClient {
	private Socket socket;
	private BufferedReader br;
	private PrintWriter pw;
	private Scanner sc;

	public void go() throws UnknownHostException, IOException {
		try {
			socket = new Socket(IP.LOCAL, 5432);
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(socket.getOutputStream(),true);
			sc = new Scanner(System.in);
			
			ReceiverWorker rw = new ReceiverWorker();
			Thread thread = new Thread(rw);
			thread.setDaemon(true);
			
			thread.start();
			
			System.out.println("**ChatClient가 서버에 접속**");
			
			while (true) {
	
				//System.out.print("서버에 보낼 메세지:");
				String message = sc.nextLine();
				pw.println(message);
		
				if (message.trim().equals("종료")) {
					System.out.println("**ChatClient 종료합니다**");
					break;
				}
			}
		}finally {
			closeAll();
		}
	}
	
	class ReceiverWorker implements Runnable {

		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);
				System.out.print("서버에 보낼 메세지:");
			}
		}
	}

	public static void main(String[] args) {
		ChatClient client = new ChatClient();
		try {
			client.go();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void closeAll() throws IOException {
		if (pw != null)
			pw.close();
		if (sc != null)
			sc.close();
		if (br != null)
			br.close();
		if (socket != null)
			socket.close();
	}

}