(자바)소켓 통신 - 양방향 채팅

2021. 6. 23. 00:33(구)공부/JAVA

728x90

 

중앙 서버를 두고 여러 클라이언트가 접속하여 채팅하는 프로그램

 

동작 흐름

서버 기동 -> 클라이언트의 접속 요청 대기 -> 클라이언트에서 접속 요청-> 통신 소켓 생성

 

클라이언트 코드

public class Client extends Common {
	public static void main(String[] args) {
		System.out.println("스프링 채팅방 클라이언트를 시작합니다...");
		Client aClient = new Client();
		aClient.execute();
	}

	Socket 					cSock;
	DataInputStream 		inStream;
	DataOutputStream 		outStream;
	
	private void execute() {
		try {
			cSock = new Socket(SERVER_IP, SERVER_PORT);
		} catch (UnknownHostException e) {
			System.out.printf("서버를 찾을 수 없음 : [%s]\n", SERVER_IP);
			return;
		} catch (IOException e) {
			System.out.println("서버가 실행되고 있지 않습니다...");
			return;
		}

		try {
			inStream 	= new DataInputStream(cSock.getInputStream());
			outStream 	= new DataOutputStream(cSock.getOutputStream());
			
			// 채팅 시작 : 2개의 모드로 분리
			
			// Write Mode는 바로 구현
			Scanner aScanner = new Scanner(System.in);
			String aMessage;
			while(true) {
				System.out.print("채팅에 사용할 닉네임 : ");
				aMessage = aScanner.nextLine();
				if(aMessage.isEmpty()) {
					System.out.println("올바른 닉네임이 아닙니다...");
					continue;
				}
				outStream.writeUTF(aMessage);	// 닉네임을 서버로 전송
				break;
			}
			
			// Read Mode 쓰레드 생성 : 내부의 run()이 쓰레드로 호출됨
			new Thread(this).start();
			
			while(true) {
				System.out.print("대화 > ");
				aMessage = aScanner.nextLine();
				outStream.writeUTF(aMessage);
			}
			
		} catch (IOException e) {
			System.out.println("채팅 서버 문제로 종료합니다...");
			System.exit(0);
		}
	}

	@Override
	public void run() { // 채팅 : Read Mode
		String aMessage;
		while(true) {
			try {
				aMessage = inStream.readUTF();
				System.out.println("\n\n"+aMessage+"\n");
				System.out.print("대화 > ");
			} catch (IOException e) {
				System.out.println("채팅 서버 문제로 종료합니다...");
				System.exit(0);
			}
		}
	}
}

공통 코드

import java.text.SimpleDateFormat;
import java.util.Date;

public class Common implements Runnable {
	public static final String STOPMESSAGE 	= "EXIT";
	public static final String SIP 			= "자신의 IP";
	public static final int    SPORT 		= 7777; // 자신이 사용할 포트를 지정. 낮은 번호대는 
    //사용처가 정해진 포트가 많으므로 조심한다.
	
	
	@Override
	public void run() {
	}
	
	public String getTime() {
		SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
		return f.format(new Date());
	}
}

서버코드

class Phone extends Common {
	static Vector<Phone> 	aList = new Vector<Phone>();// 클라이언트 객체 목록
	String 					cNickName;
	Socket 					cSock;
	DataInputStream 		inStream;
	DataOutputStream 		outStream;
	
	@Override
	public void run() {	// 클라이언트와의 채팅 구현
		try {
			outStream.writeUTF("[공지사항]:스프링 과정 채팅 서버에 입장하셨습니다...");
			//대화 :chatting
			String aMessage;
			cNickName = inStream.readUTF();
			aMessage = String.format("[공지사항]:%s님이 입장하셨습니다...",cNickName);
			System.out.printf("\n%s\n",aMessage);
			System.out.print("공지사항 입력 : ");
			SendMessage(aMessage, this);

			while(true) {
				aMessage = inStream.readUTF();
				aMessage = String.format("[%s]%s",cNickName, aMessage);
				System.out.printf("\n%s\n",aMessage);
				System.out.print("공지사항 입력 : ");

				SendMessage(aMessage, this);
			}

			//outStream.writeUTF("[공지사항]:채팅방을 나가셨습니다...");
		} catch (IOException e) {
			aList.remove(this);
			String aMessage = String.format("[알림]:[%s]님이 나가셨습니다...", cNickName);
			System.out.printf("\n%s\n", aMessage); 	// 서버에 통보
			SendMessage(aMessage, this);			// 클라이언트들에게 통보
			System.out.println("현재 접속자 수 : " + aList.size());
			System.out.print("공지사항 입력 : ");
		}
	}
	
	public static void SendMessage(String aMessage, Phone aPhone) {
		for(Phone Temp : aList) {
			if(aPhone == Temp) {
				continue;
			}
			try {
				Temp.outStream.writeUTF(aMessage);
			} catch (IOException e) {
				System.out.println("이거 뜨면 여기 다시 코딩");
			}
		}
	}

	public Phone(Socket cSock) { // 소켓에 해당하는 인풋, 아웃풋 스트림을 가져 옴
		this.cSock = cSock;
		try {
			inStream 	= new DataInputStream(cSock.getInputStream());
			outStream 	= new DataOutputStream(cSock.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
		aList.add(this);
		System.out.println("\n현재 접속자 수 : " + aList.size());
	}
}

public class Server extends Common {
	public static void main(String[] args) {
		System.out.println("Server Start...");
		Server aServer = new Server();
		aServer.execute();
	}
	
	@Override
	public void run() {
		Scanner aScanner = new Scanner(System.in);
		String sMessage;
		
		System.out.println("\n"+getTime()+"공지사항 기능이 활성화 되었습니다.");
		while(true) {
			System.out.print("공지사항 입력 : ");
			sMessage = aScanner.nextLine();
			if(sMessage.equals(STOP_MESSAGE)) {
				Phone.SendMessage(sMessage, null);
				break;
			}
			sMessage = String.format("[공지사항]:[%s]", sMessage);
			System.out.printf("\n%s\n", sMessage);
			Phone.SendMessage(sMessage, null);
		}
		System.out.println("종료를 위해 공지사항 기능을 비활성화 합니다.");
		aScanner.close();
		
		
		// 쓰레드를 강제 정지 시키고
		// 스트림과 소켓을 모두 닫는다.
		for(Phone Temp : Phone.aList) {
			//Temp.interrupt();
			try {
				Temp.inStream.close();
				Temp.outStream.close();
				Temp.cSock.close();
			} catch (IOException e) {
			}
		}
		
		Phone.aList.removeAllElements();
		
		try {
			sSocket.close();
			sSocket = null;
		} catch (IOException e) {
			System.out.println("소켓을 닫는 중에 IO 예외가 발생하였습니다...");
		}
		
		System.out.println("서버 쓰레드 종료합니다...");
	}

	private ServerSocket sSocket;
	
	public Server() {
	}
	public void execute() {
		createSocket();

		new Thread(this).start();	// run을 쓰레드로 실행:Scanner 활성화
		while(true) {
			System.out.println(getTime()+"연결요청을 기다립니다.");
			System.out.print("공지사항 입력 : ");
			try {
				Socket socket = sSocket.accept();
				
				if (sSocket == null) {
					System.out.println("채팅 서버 종료합니다...");
					break;
				}
				
				System.out.println(
						"\n"
						+ getTime()
						+ socket.getInetAddress().toString().substring(1)
						+ "로부터 연결요청이 들어왔습니다."
						);
				
				//////////////////////////////////////
				// 클라이언트 쓰레드 생성
				new Thread(new Phone(socket)).start();
				//////////////////////////////////////
			} catch (IOException e) {
				System.out.println("IO 문제 발생");
				break;
			}
		}
	}
	
	private void createSocket() {
		try {
			sSocket = new ServerSocket(SERVER_PORT);
		} catch (IllegalArgumentException e) {
			System.out.println("잘못된 포트 번호 사용");
			return;
		} catch (IOException e) {
			System.out.println("프로세서에서 포트가 선점되었습니다["+SERVER_PORT+"]");
			System.out.println("서버가 실행 불가능하여 종료합니다...");
			System.exit(0);
		}
		System.out.println(getTime()+"서버가 준비되었습니다...");
	}
	
}

 

 

 

 

728x90

'(구)공부 > JAVA' 카테고리의 다른 글

JDBC  (0) 2021.07.08
Swing - window builder  (0) 2021.07.07
JUnit  (0) 2021.07.07