자바 TCP 소켓 프로그래밍(채팅프로그램)

우 동현·2021년 9월 22일
1

TCP 소켓 프로그래밍(채팅프로그램) 구현과정

흐름도

소스코드
Client

public class ChatClient {
	private static final int SERVER_PORT = 6000;
	public static void main(String[] args) {
		Socket socket = null;
		Scanner scanner = null;
		String line;
		try {
			scanner = new Scanner(System.in);
			//소켓 객체 생성
			socket = new Socket();
			String hostAddress = InetAddress.getLocalHost().getHostAddress();
			//connect
			socket.connect(new InetSocketAddress(hostAddress, SERVER_PORT));
			log("채팅방에 참가했습니다.");
			System.out.print("닉네임>>");
			String nickname = scanner.nextLine();	//block
			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
			pw.println("join:" + nickname +"\n");
		
			//6. 쓰레드시작 :읽기
			new ChatClientThread(socket).start();
			//쓰기
			while(true){
				line = scanner.nextLine();//block
				// 8. quit 프로토콜 처리
				if("quit".equals(line)) {
						ChatClient.log("채팅방을 나갔습니다.");
					break;
				}else {
				// 9.메시지 처리 
					pw.println("message:"+line);
				}
			}
		} catch (IOException e) {
			log("error:" + e);
		}finally {
			try {
				if(scanner != null) {
					scanner.close();
				}
				//close
				if(socket != null && socket.isClosed() == false) {
					socket.close();					
				}
			} catch (IOException e) {
				log("ChatServer 소켓종료시 에러");
			}
		}
	}
	public static void log(String log) {
		System.out.println("[Chat Client] " + log);
	}
}

ClientThread

public class ChatClientThread extends Thread {
	private Socket socket;
	BufferedReader br;
	public ChatClientThread(Socket socket) {
		this.socket = socket;
	}
	@Override
	public void run() {
		try {		
			String line = null;
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
				while (true) {
				// 읽고 채팅방에 써주기
					line = br.readLine();
					System.out.println(line);		
				}
		} catch (IOException e) {
			ChatClient.log("서버와 연결이 종료되었습니다." );
		} 
	}
}

Server

public class ChatServer {
	private static final int PORT = 6000;
	static List<Writer> listWriters;
	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		List<Writer> listWriters = new ArrayList<Writer>();
		try {
			System.out.print(">>");
			// 서버소켓 객체 생성
			serverSocket = new ServerSocket();
			// bind
			String hostAddress = InetAddress.getLocalHost().getHostAddress();
			serverSocket.bind(new InetSocketAddress(hostAddress, PORT));
			log("연결 기다림 " + hostAddress + ":" + PORT);
			// 요청대기
			while (true) {
				// accept
				Socket socket = serverSocket.accept();
				// read,write
				new ChatServerThread(socket, listWriters).start();
			}
		} catch (IOException e) {
			log("ChatServer 대기중에러: " + e);
		} finally {
			try {
				// close
				if (serverSocket != null && serverSocket.isClosed() == false) {
					serverSocket.close();
				}
			} catch (IOException e) {
				log("ChatServer 소켓종료시 에러");
			}
		}
	}
	public static void log(String log) {
		System.out.println("[ChatServer] " + log);
	}
}

ServerThread

public class ChatServerThread extends Thread {
	private Socket socket;
	private String nickname;
	List<Writer> listWriters;
	
	public ChatServerThread(Socket socket, List<Writer> listWriters) {
		this.socket = socket;
		this.listWriters = listWriters;
	}
	@Override
	public void run() {
		// 1. Remote Host Information
		InetSocketAddress inetRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
		String remoteHostAddress = inetRemoteSocketAddress.getAddress().getHostAddress();
		int remoteHostPort = inetRemoteSocketAddress.getPort();
		ChatServer.log("connected by client[" + remoteHostAddress + ":" + remoteHostPort + "]");
		try {
			// 2. 스트림 얻기
			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
			// 3. 요청 처리
			while (true) {
				String request = br.readLine();
				if (request == null) {
					ChatServer.log("클라이언트로 부터 연결 끊김");
					doQuit(pw);
					break;
				}
				// 4. 프로토콜 분석
				String[] tokens = request.split(":");
				if ("join".equals(tokens[0])) {		
					doJoin( tokens[1], pw);	
				}else if("message".equals(tokens[0])) {
					doMessage(tokens[1]);
				}else if("quit".equals(tokens[0])) {
					doQuit(pw);
				}
			}
		} catch (IOException e) { // 2. 스트림 얻기에대한 익셉션
			ChatServer.log("stream error: " + e);
		}
	}
	private void doQuit(Writer writer) {
	removeWriter(writer);
	broadcast( this.nickname + "님이 퇴장 하였습니다.");
	}
	private void removeWriter(Writer writer) {
		synchronized(listWriters) {
			listWriters.remove(writer);
		}
	}
	private void doMessage(String message) {
			broadcast(nickname + " : " + message);
	}
	private void doJoin(String nickname, Writer writer) {
		this.nickname =	nickname;
		String data = nickname + "님이 참여하였습니다.";
		addWriter(writer);
		broadcast(data);
		((PrintWriter)writer).println("join:ok");
	}
	private void addWriter(Writer writer) {
		synchronized(listWriters) {
			listWriters.add(writer);
		}
	}
	private void broadcast(String data) {
		synchronized(listWriters) {
			for(Writer writer: listWriters) {
				PrintWriter printWriter = (PrintWriter)writer;
				printWriter.println(data);
				printWriter.flush();
			}
		}
	}
}

0개의 댓글