본문 바로가기
프로그래밍/Java

[자바]채팅 프로그램(여러 사용자 동시 접속)

by 청량리 물냉면 2021. 8. 19.
반응형

TCPServer.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPServer implements Runnable {
	private static Socket clientSocket; // 각각의 클라이언트를 구분하기 위함
	static PrintWriter out = null;
	static ArrayList<Socket> list = new ArrayList<Socket>(); // 클라이언트 쓰레드를 담을 ArrayList 생성
	static int UserNum = 0; // 서버에서 클라이언트에게 번호를 부여하기 위한 변수

	public TCPServer(Socket clientSocket) { // 멀티 쓰레드 환경을 구축하기 위한 생성자. 각각의 개별 쓰레드를 생성
		this.clientSocket = clientSocket;
		list.add(clientSocket); // 쓰레드들을 리스트에 추가하는 부분
	}

	public static void main(String[] args) {
		ExecutorService eService = Executors.newFixedThreadPool(5);
		// 쓰레드 생성. 6번째 접속부터는 BLOCKING-참가자를 6명으로 제한

		System.out.println("#서버 시작");

		try (ServerSocket sSocket = new ServerSocket(10000)) {
			// try-with-resource문 사용 시 자동으로 자원 해제
			while (true) { // 접속하는 클라이언트들에게 무한 서비스
				System.out.println("#연결 대기 중 ......");
				clientSocket = sSocket.accept();
				// 연결 수락. client 연결정보, ip주소 담음
				TCPServer tes = new TCPServer(clientSocket);
				// 개별 쓰레드 객체 생성. 연결 수락시 접속한 클라이언트의 정보 담김
				// new Thread(tes).start();
				// 각각의 쓰레드가 식별가능한 인수를 갖게 된다, 개별적인 수행으로 효율적이지는 않다
				eService.submit(tes);
				// 더 효율적인 방법
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("#서버 종료");
		eService.shutdown();
	}

	public void run() {
		// 각각의 클라이언트들에 대한 피드백을 하는 run함수
		// 접속한 클라이언트들에 대응가능한 송수신용 버퍼와 각종 변수들이 들어있음
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
			// 수신 버퍼
			// OutputStream : 서버에서 클라이언트로 메세지 보내기
			OutputStream out = clientSocket.getOutputStream();
			PrintWriter writer = new PrintWriter(out, true);
			// out = new PrintWriter(clientSocket.getOutputStream(), true); //송신 버퍼

			UserNum++;
			System.out.println("#클라이언트 : " + UserNum + "(" + Thread.currentThread() + ")" + " 연결됨!");

			String UserID = br.readLine(); // 사용자에게서 id를 입력받아 설정
			String inputLine; // 클라이언트가 보낸 값 저장

			writer.println("#[" + UserID + "]님이 접속하셨습니다.");
			System.out.println("#[" + UserID + "]님이 접속하셨습니다.");

			while ((inputLine = br.readLine()) != null) {
				// readLine(): client가 보낸 msg를 br에서 한줄씩 꺼냄
				// System.out.println("[" + UserID + "] " + inputLine);
				// 클라이언트에게서 받아온 정보를 한줄씩 출력

				for (int i = 0; i < list.size(); i++) {
					// ArrayList의 사이즈만큼 for문을 돌리면서 모든 클라이언트에게 메시지 전송
					out = list.get(i).getOutputStream();
					// ArrayList의 i번째 getOutputStream()요소를 가져와 out객체에 대입한다.
					writer = new PrintWriter(out, true);
					// 새로운 printWriter객체를 생성
					writer.println("[" + UserID + "] " + inputLine);
					// ArrayList의 i번째 요소를 클라이언트에게 송신한다.
				}
			}
			out.close();
			System.out.println("#클라이언트 : " + Thread.currentThread() + " 종료됨!");
		} catch (IOException ex) {
			ex.printStackTrace();
		}

	}
}

 

TCPClient.java

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class TCPClient {

	Socket cSocket;
	BufferedReader br;
	PrintWriter out;
	Scanner scv;

	public TCPClient() { // 메시지 송신
		System.out.println("서버에 접속하는 중입니다.");
		try {
			// accept() 호출 후 쓰레드 만들어지고, Array List에 추가된다.
			InetAddress localAddress = InetAddress.getLocalHost();

			cSocket = new Socket(localAddress, 10000);// Socket cSocket = new Socket("127.0.0.1", 9000);
			// 10000번 포트와 로컬 주소를 사용하는 cSocket이라는 새로운 소켓을 생성
			SocketThread st = new SocketThread(); // 소켓 쓰레드의 새로운 객체인 st라는 쓰레드를 생성한다.
			st.start(); // st를 실행시킨다.
			// Test클래스가 Thread 클래스를 상속했다.
			// Thread 클래스의 run 메소드를 구현하면 st.start() 실행 시 st객체의 run 메소드가 수행이 된다.
			out = new PrintWriter(cSocket.getOutputStream(), true);
			scv = new Scanner(System.in);

			// 사용자 아이디 지정하는 부분
			String UserID = "";
			System.out.println("#서버에 연결됨!");
			System.out.println("#사용할 ID를 입력해주세요.");
			out.print(UserID);

			while (true) {
				String inputLine = scv.nextLine(); // 사용자에게서 서버로 보낼 값을 입력받는 변수
				out.println(inputLine); // 입력받은 키보드 값을 서버로 전송
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	class SocketThread extends Thread { // 메시지 수신
		public void run() {
			try {
				br = new BufferedReader(new InputStreamReader(cSocket.getInputStream())); // 송신 버퍼 객체 생성
				String response = null; // 송신된 메시지를 저장하기 위한 변수
				while ((response = br.readLine()) != null) { // br에 읽을 메시지가 있는 한 무한반복
					System.out.println(response); // br 객체에 저장된 문자열을 한 줄 씩 받아와서 출력
				}
			} catch (Exception e) {
			}
		}
	}

	public static void main(String[] args) {
		new TCPClient(); // 메인문에서 생성자 실행시켜주기
	}
}

 

실행화면

 

 

반응형