고급JAVA 15강 - 네트워크(TCP, UDP)

Whatever·2021년 11월 17일
0

고급 JAVA

목록 보기
15/32

멀티 채팅방 만들기

서버

public class TcpMultiChatServer {
	// 접속한 클라이언트들을 저장할 Map객체 선언
	// ==> key값 : 접속한 사람의 이름, value값 : 접속한 클라이언트의 socket객체
	private Map<String, Socket> clientMap;
	
	// 생성자
	public TcpMultiChatServer() {
		// clientMap을 동기화 처리가 되도록 생성한다.
		clientMap = Collections.synchronizedMap(new HashMap<String, Socket>());
	}
	
	//clientMap에 저장된 전체 사용자에게 메시지를 전송하는 메서드
	private void sendToAll(String msg) {
		// clientMap의 데이터 개수만큼 반복
		for(String name : clientMap.keySet()) {
			try {
				// 각 사용자의 Socket을 이용하여 OutputStream객체를 구한다.
				DataOutputStream dos = new DataOutputStream(
						clientMap.get(name).getOutputStream());
                        //소켓을 꺼내오고 outputstream가져오기
				
				dos.writeUTF(msg);
				
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		
	}
	
	public void serverStart() {
		ServerSocket server = null;
		Socket socket = null;
		try {
			server = new ServerSocket(7777);
			System.out.println("서버가 시작되었습니다.");

			while(true) {
				socket = server.accept(); //클라이언트의 접속을 기다린다.
				System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() 
				+ "]에서 접속했습니다...");
				
				//메시지를 받아서 전체에게 전송하는 Thread객체를 생성하여 작동시킨다.
				ServerReceiver serverThread = new ServerReceiver(socket);
				serverThread.start();
			}
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			if(server!=null)try {server.close();}catch(Exception e) {}
		}
	}
	
	
	
	public static void main(String[] args) {
		new TcpMultiChatServer().serverStart();
	}
	
	// Inner Class로 서버에서 클라이언트로 메시지를 전송하는 Thread를 만든다.
	class ServerReceiver extends Thread{
		private Socket socket;
		private DataInputStream dis;
		private DataOutputStream dos;
		
		public ServerReceiver(Socket socket) {
			this.socket = socket;
			
			try {
				// 수신용 객체 생성
				dis = new DataInputStream(socket.getInputStream());
				
				// 송신용 객체 생성
				dos = new DataOutputStream(socket.getOutputStream());
			} catch (Exception e) {
				// TODO: handle exception
			}
		} //생성자 끝...
		
		@Override
		public void run() {
			String name = "";
			try {
				while(true) {
					//클라이언트가 접속이 완료되면 최초로 사용자의 이름을 전송하는데
					//이 이름이 중복되는지 여부를 feedBack으로 클라이언트에게 보내준다.
					name = dis.readUTF();
					
					if(clientMap.containsKey(name)) { //이름이 중복될 때
						dos.writeUTF("이름중복");
					}else { //이름이 중복되지 않을 때..
						dos.writeUTF("OK");
						break; //반복문 탈출
					}
				} //while문 끝...
				
				// 현재 접속해 있는 다른 클라이언트들에게 대화명(이름)을 이용해서 대화방 참여 메시지를 보낸다.
				sendToAll("[" + name + "]님이 대화방에 입장했습니다...");
				
				// 대화명과 클라이언트의 Socket객체를 Map에 저장한다.
				clientMap.put(name, socket);
				
				System.out.println("현재 서버 접속자 수 : " + clientMap.size()+ "명");
				
				// 한 클라이언트가 보낸 메시지를 다른 클라이언트에게 전송해준다.
				while(dis!=null) {//사용자가 보낸 메시지
					sendToAll(dis.readUTF());
				}
				
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}finally {
				// 이 finally영역이 실행된다는 것은 클라이언트가 접속을 종료하겠다는 의미이다.
				
				// 사용자목록에서 해당 클라이언트를 삭제한다.
				clientMap.remove(name);

				// 다른 사용자에게 대화방을 나갔다는 메시지 전송
				sendToAll("[" + name + "]님이 대화방을 나갔습니다.");
				
				System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() 
				+ "]에서 접속을 종료했습니다...");
				
				System.out.println("현재 서버 접속자 수 : " + clientMap.size()+ "명");
				
			}
		}
		
	}

}

클라이언트

public class TcpMultiChatClient {

	public void clientStart() {
		try {
			String serverIp = "localhost";
			
			Socket socket = new Socket(serverIp, 7777);
			System.out.println("서버에 연결되었습니다.");
			System.out.println();
			
			//메시지 전송용 쓰레드 생성
			ClientSender sender = new ClientSender(socket);
			
			//메시지 수신용 쓰레드 생성
			ClientReceiver receiver = new ClientReceiver(socket);
			
		} catch (Exception e) {
			// TODO: handle exception
		} 
		
	}
	
	public static void main(String[] args) {
		new TcpMultiChatClient().clientStart();
		
	}

	// -----------------------------------------
	// 메시지 전송용 쓰레드
	class ClientSender extends Thread{
		private Socket socket;
		public DataInputStream dis;
		public DataOutputStream dos;
		
		private String name;
		private Scanner scan;
		
		// 생성자
		public ClientSender(Socket socket) {
			this.socket = socket;
			scan = new Scanner(System.in);
			
			try {
				dis = new DataInputStream(socket.getInputStream());
				dos = new DataOutputStream(socket.getOutputStream());
				
				if(dos!=null) {
					// 클라이언트가 처음 실행되면 자신의 대화명(이름)을 입력받아 서버로 전송하고 
					// 대화명의 중복여부를 feedBack으로 받아서 확인한다.
					
					System.out.println("대화명 : ");
					String name = scan.nextLine();
					while(true) {
						
						dos.writeUTF(name); // 대화명 전송
						
						String feedBack = dis.readUTF(); // 대화명 중복 여부를 받는다.
						
						if("이름중복".equals(feedBack)) { //대화명이 중복되면...
							System.out.println(name + "은 대화명이 중복됩니다...");
							System.out.println("다른 대화명을 입력하세요.");
							
							System.out.println("대화명 : ");
							name = scan.nextLine();
							
						}else { // 중복되지 않을 때
							this.name = name;
							System.out.println(name + " 이름으로 대화방에 입장했습니다.");
							break;
						}
					}// while문의 끝...
					
					
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}// 생성자
		
		@Override
		public void run() {
			try {
				while(dos!=null) {
					//키보드로 입력한 메시지를 서버로 전송한다.
					dos.writeUTF("[" + name + "]" + scan.nextLine());
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}
	//------------------------------------------------------
	
	// 메시지 수신용 쓰레드
	class ClientReceiver extends Thread{
		private Socket socket;
		private DataInputStream dis;
		
		// 생성자
		public ClientReceiver(Socket socket) {
			this.socket = socket;
			try {
				dis = new DataInputStream(socket.getInputStream());
			} catch (Exception e) {
				// TODO: handle exception
			}
		} //생성자 끝...
		
		@Override
		public void run() {
			try {
				while(dis!=null) {
					//서버가 보내온 메시지를 받아서 화면에 출력한다.
					System.out.println(dis.readUTF());
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	
}

UDP

UDP방식 : 비연결 지향, 신뢰성이 없다. 데이터가 순서대로 도착한다는 보장이 없다.
그렇지만 TCP보다 속도가 빠르다.
DatagramSocket객체의 DatagramPacket객체를 이용해서 통신한다.
- DatagramSocket : 데이터의 송수신과 관련된 작업을 수행한다.(우체부)
- DatagramPacket : 주고받는 데이터와 관련된 작업을 수행한다.(소포)
==> 수신을 위한 생성자와 송신을 위한 생성자를 따로 제공한다.

서버

public class UdpServer {

	public static void main(String[] args) {
		try {
			// 통신할 포트번호를 지정하여 소켓을 생성한다.
			DatagramSocket socket = new DatagramSocket(8888);
			
			// 수신용 패킷변수와 송신용 패킷변수를 선언
			DatagramPacket inpacket, outpacket;
			
			System.out.println("서버 실행중...");
			
			while(true) {
				// 데이터가 저장될 byte형 배열 선언
				byte[] bMsg = new byte[512];
				
				// 수신용 패킷객체 생성
				//	==> 데이터가 저장될 byte형 배열, 배열의 길이를 이용하여 생성한다.	
				inpacket = new DatagramPacket(bMsg, bMsg.length);
				
				// 데이터를 수신한다.
				// 이 메서드는 데이터가 올 때까지 기다린다.
				// 수신된 데이터의 패킷정보는 지정한 패킷변수에 저장된다.
				socket.receive(inpacket);
				
				// 수신받은 패킷에서 상대방의 IP주소, 포트번호 등을 알 수 있다.
				InetAddress address = inpacket.getAddress(); // 누구에게 전송할지 알아야해서
				int port = inpacket.getPort();
				
				System.out.println("상대방의 IP정보 : " + address);
				System.out.println("상대방의 port번호 : " + port);
				
				// 상대방이 보낸 메시지 화면에 출력하기 
				// inpacket.getLength() ==> 실제 읽어온 길이
				// inpacket.getData()   ==> 실제 읽어온 데이터를 byte배열로 반환한다.
				// 실제 데이터는 수신용 패킷객체에 지정한 byte형 배열에도 저장된다.
				
				// 방법1
				//String msg = new String(bMsg, 0, inpacket.getLength());
				
				// 방법2
				String msg = new String(inpacket.getData(), 0, inpacket.getLength());
				
				System.out.println("상대방이 보낸 메시지 : " + msg);
				System.out.println();
				
				//------------------------------------------------
				// 상대방에게 메시지 보내기(수신받은 메시지 그대로 송신한다.)
				
				// 송신할 메시지를 byte형 배열로 반환한다.
				byte[] sendMsg = msg.getBytes("utf-8");
				
				// 송신용 패킷 객체 생성
				// ==> 전송할 데이터가 저장된 byte형 배열, 전송할 자료의 길이(배열의 길이), 
                                       상대방주소정보, 상대방포트번호
				// 	   위의 4가지를 지정하여 생성한다.
				outpacket = new DatagramPacket(sendMsg, sendMsg.length, address, port);
				
				// 송신하기 ==> send() 메서드 사용
				socket.send(outpacket);
				
				System.out.println("송신 완료...");
				
			}
			
		} catch (Exception e) {
			// TODO: handle exception
		}

	}

	
}

클라이언트

public class UdpClient {

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		
		// 송신용, 수신용 패킷객체 변수 선언
		DatagramPacket inpacket, outpacket;
		
		// 수신받은 데이터가 저장될 byte형 배열
		byte[] bMsg = new byte[512];
		
		try {
			//소켓객체 생성
			DatagramSocket socket = new DatagramSocket();
			
			// 접속할 곳의 주소 생성한다.
			InetAddress address = InetAddress.getByName("localhost");
			
			while(true) {
				// 전송할 메시지 입력
				System.out.print("전송할 메시지 입력 : ");
				String msg = scan.nextLine();
				
				if("/end".equals(msg)) {
					break;
				}
				
				// 전송할 패킷 객체 생성
				outpacket = new DatagramPacket(msg.getBytes("utf-8"), msg.getBytes("utf-8").length,
						address, 8888);
				
				// 전송
				socket.send(outpacket);
				
				//------------------------------------------
				
				// 서버에서 보내온 메시지 받아서 출력하기 
				
				// 수신용 패킷객체 생성
				inpacket = new DatagramPacket(bMsg, bMsg.length);
				
				// 수신
				socket.receive(inpacket);
				
				System.out.println("서버의 응답 데이터 " + new String(bMsg, 0, inpacket.getLength()));
				System.out.println();
				
			} // while문 끝...
			
			System.out.println("통신 끝...");
			
			
		} catch (Exception e) {
			// TODO: handle exception
		}

	}

}

0개의 댓글