JAVA/NetWork

MultiChat

lavender1122 2024. 2. 17. 11:56

MultiChatClient

Socket 객체 생성& 송수신 스레드 생성 및 실행

public void start() {
		//서버와 통신할 수 있는 Socket 만들고 서버 접속
		Socket 변수명 = null;
		try {
			socket = new Socket("IP주소", 포트번호);
			System.out.println("멀티챗 서버에 접속했습니다.");
			
			//송신용 스레드 생성 및 실행
			ClientSender 변수명 = new ClientSender(Socket_변수명);
			ClientSender_변수명.start();
			
			//수신용 스레드 생성 및 실행
			ClientReceiver 변수명 = new ClientReceiver(Socket_변수명);
			ClientReceiver_변수명.start();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
  • 예시
더보기

 

public void start() {
		//서버와 통신할 수 있는 Socket 만들고 서버 접속
		Socket socket = null;
		try {
			socket = new Socket("192.168.144.~", 7777);
			System.out.println("멀티챗 서버에 접속했습니다.");
			
			//송신용 스레드 생성 및 실행
			ClientSender sender = new ClientSender(socket);
			sender.start();
			
			//수신용 스레드 생성 및 실행
			ClientReceiver receiver = new ClientReceiver(socket);
			receiver.start();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

ClientSender extends Thread

  • 메시지 전송을 위한 스레드 클래스
  • getOutputStream() 메소드 : 전송할 데이터 출력
  • writeUTF(String str) : UTF-8 형식으로 코딩된 문자열 출력
		private DataOutputStream 변수명;
		private Scanner scan;
		
		public ClientSender(Socket socket) {
			scan = new Scanner(System.in);
			try {
				DataOutputStream_변수명 = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
        		@Override
		public void run() {
			try {
				if(DataOutputStream_변수명 != null) { //대화명
					//시작하자마자 자신을 대화명을 서버로 전송하기
					System.out.println("대화명 >>");
					DataOutputStream_변수명.writeUTF(scan.nextLine());
				}
				while (DataOutputStream_변수명 != null) {
					//이제부터는 일반 채팅메시지 전송하기
					DataOutputStream_변수명.writeUTF(scan.nextLine());
				}
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
  • 예시
더보기
	class ClientSender extends Thread{
		private DataOutputStream dos;
		private Scanner scan;
		
		public ClientSender(Socket socket) {
			scan = new Scanner(System.in);
			try {
				dos = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			try {
				if(dos != null) { //대화명
					//시작하자마자 자신을 대화명을 서버로 전송하기
					System.out.println("대화명 >>");
					dos.writeUTF(scan.nextLine());
				}
				
				while (dos != null) {
					//이제부터는 일반 채팅메시지 전송하기
					dos.writeUTF(scan.nextLine());
				}
				
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

 

ClientReceiver extends Thread

  • 메시지 수신을 위한 스레드 클래스
  • getInputStream() : 전송하는 데이터를 읽음
  • String readUTF() : UTF-8 형식으로 코딩된 문자열 읽음
class ClientReceiver extends Thread{
		private DataInputStream 변수명;
		
		public ClientReceiver(Socket socket) {
			try {
				DataInputStream_변수명 = new DataInputStream(socket.getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			try {
				while (DataInputStream_변수명 != null) {
					//서버로부터 받은 메시지 콘솔에 출력하기
					System.out.println(DataInputStream_변수명.readUTF());
				}
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • 예시
더보기
	class ClientReceiver extends Thread{
		private DataInputStream dis;
		
		public ClientReceiver(Socket socket) {
			try {
				dis = new DataInputStream(socket.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			try {
				while (dis != null) {
					//서버로부터 받은 메시지 콘솔에 출력하기
					System.out.println(dis.readUTF());
				}
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

Main

public static void main(String[] args) {
		new MultiChatClient().start();
	}
}

MultiChatServer

serverStart

  • 대화명을 키, 해당 소켓을 값으로 하는 Map객체 변수 선언 & 생산자 선언(컬렉션 동기화)
  • 다른 client가 동시에 접속할 수 있으므로 동기화 진행 
스레드 동기화란?
하나의 객체을 여러개의 스레드가 공유해서 사용할 때 하나의 스레드가 객체를 사용중이면 다른 스레드를 사용할 수 없으며 lock 걸어두는 것
	private Map<String, Socket> 변수명;
    
	public MultiChatServer() {
      Map<String, Socket>_변수명 = Collections.synchronizedMap(new HashMap<String, Socket>());
   }
     ServerSocket_변수명 = null;
	 Socket Socket_변수명 = null;
     try {
         ServerSocket_변수명 = new ServerSocket(포토번호);
         
         while(true) { //무한 루프
          // 클라이언트의 접속을 대기
          Socket_변수명 = ServerSocket_변수명.accept();
            
          System.out.println("[" + Socket_변수명.getInetAddress() + " : " 
                             + Socket_변수명.getPort() + "] 에서 접속하였습니다.");
           // 사용자가 보내준 메시지를 받아서 처리하는 스레드 생성 및 실행
           ServerRecevier 변수명 = new ServerRecevier(Socket_변수명);
		   ServerRecevier_변수명.start();
           }
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }
  • 예시
더보기
   private Map<String, Socket> clients;
   
   public MultiChatServer() {
      clients = Collections.synchronizedMap(new HashMap<String, Socket>());
   }
 	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() + "] 에서 접속하였습니다.");
            
            // 사용자가 보내준 메시지를 받아서 처리하는 스레드 생성 및 실행
            ServerRecevier sr = new ServerRecevier(socket);
            sr.start();
         }
         
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }

 

sendMessage(String msg)

  • Map에 저장된 모든 접속한 사람들에게 안내메서지 전송하기 위한 메서드
  • param (msg) : 전송할 메서지
  • Iterator 이용해서 Map에 있는 키값 전체 출력
  • DataOutputStream : 해당 사용자에게 메시지 보내기
    • getOutputStream 객체 만들어서 꺼냄
 public void sendMessage(String msg){
	   Iterator<String> 변수명 = Map<String, Socket>_변수명.keySet().iterator();
	   
	   while (Iterator_변수명.hasNext()) {
		try {
			String 변수명 = Iterator_변수명.next(); // 대화명
			 
			// 해당 사용자에게 메시지 보내기
			//getgetOutputStream 객체 만들어서 꺼냄
			new DataOutputStream(
					clients.get(변수명).getOutputStream()).writeUTF(msg);
			
		}  catch (IOException ex) {
	         ex.printStackTrace();
	      }
	}
   }
더보기
   public void sendMessage(String msg){
	   Iterator<String> it = clients.keySet().iterator();
	   
	   while (it.hasNext()) {
		try {
			String name = it.next(); // 대화명
			 
			// 해당 사용자에게 메시지 보내기
			//getgetOutputStream 객체 만들어서 꺼냄
			new DataOutputStream(
					clients.get(name).getOutputStream()).writeUTF(msg);
			
		}  catch (IOException ex) {
	         ex.printStackTrace();
	      }
	}
   }

 sendMessage(String msg, String from)

  • 파라미터 2개 메소드가 있어서 오버로딩으로 하나 더 생성
  • param (msg) : 전송할 메서지
  • param (from) : 메시지 보낸 대화명
public void sendMessage(String msg, String from){
	   sendMessage("[" +from + "]"+ msg);
   }

ServerRecevier extends Thread

  • 서버에서 클라이언트로부터 수신한 메세지를 처리하기 위한 클래스
class ServerRecevier extends Thread{
      private Socket 변수명;
      private DataInputStream 변수명;
      private String 변수명;
      
      public ServerRecevier(Socket socket) {
         this.Socket_변수명 = socket;
         
         try {
            DataInputStream_변수명 = new DataInputStream(socket.getInputStream());
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
      
      @Override
      public void run() {
         try {
            // 서버에서는 클라이언트가 보내온 최초의 메시지, 즉 대화명을 수신해야 한다.
            String_변수명 = DataInputStream_변수명.readUTF();
            
            // 대화명을 받아서 다른 모든 클라이언트들에게 대화방 참여 메시지를 보낸다.
            sendMessage("#" + String_변수명 + "님이 입장했습니다.");
            
            // 대화명을 사용하여 소켓객체를 Map에 저장한다.
            clients.put(String_변수명, Socket_변수명);
            
            System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");

            // 이 이후의 처리는 반복문으로 처리한다.
            // 클라이언트가 보내온 메시지(대화내용)를 접속한 다른 모든 클라이언트
            
            while (DataInputStream_변수명 != null) {
            	//name 앞에 누구인지 출력하기 위한용
            	sendMessage(DataInputStream_변수명.readUTF(),String_변수명);
			}
         } catch (IOException ex) {
            ex.printStackTrace();
         }finally {
			//이 부분이 실행된다는것은 클라이언트와 접속에 문제가 생겼다는 의미
        	// 그래서 접속종료 처리를 한다.
        	 sendMessage(String_변수명 + "님이 나가셧습니다.");
        	 
        	 // Map에서 해당 사용자를 삭제한다.
        	 clients.remove(String_변수명);
        	 
        	 System.out.println("[" + Socket_변수명.getInetAddress() + " : " 
                     + Socket_변수명.getPort() + "] 에서 종료하였습니다.");
        	 
        	 System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");
		}
      }
   }
  • 예시
더보기
class ServerRecevier extends Thread{
      private Socket socket;
      private DataInputStream dis;
      private String name;
      
      public ServerRecevier(Socket socket) {
         this.socket = socket;
         
         try {
            dis = new DataInputStream(socket.getInputStream());
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
      
      @Override
      public void run() {
         try {
            // 서버에서는 클라이언트가 보내온 최초의 메시지, 즉 대화명을 수신해야 한다.
            name = dis.readUTF();
            
            // 대화명을 받아서 다른 모든 클라이언트들에게 대화방 참여 메시지를 보낸다.
            sendMessage("#" + name + "님이 입장했습니다.");
            
            // 대화명을 사용하여 소켓객체를 Map에 저장한다.
            clients.put(name, socket);
            
            System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");

            // 이 이후의 처리는 반복문으로 처리한다.
            // 클라이언트가 보내온 메시지(대화내용)를 접속한 다른 모든 클라이언트
            
            while (dis != null) {
            	//name 앞에 누구인지 출력하기 위한용
            	sendMessage(dis.readUTF(),name);
			}
            
         } catch (IOException ex) {
            ex.printStackTrace();
         }finally {
			//이 부분이 실행된다는것은 클라이언트와 접속에 문제가 생겼다는 의미
        	// 그래서 접속종료 처리를 한다.
        	 sendMessage(name + "님이 나가셧습니다.");
        	 
        	 // Map에서 해당 사용자를 삭제한다.
        	 clients.remove(name);
        	 
        	 System.out.println("[" + socket.getInetAddress() + " : " 
                     + socket.getPort() + "] 에서 종료하였습니다.");
        	 
        	 System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");
		}
      }
   }

main

public static void main(String[] args) {
		new MultiChatServer().serverStart();
	}

원본

MultiChatServer

더보기
package kr.or.ddit.tcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class MultiChatServer {
   // 대화명을 키, 해당 소켓을 값으로 하는 Map객체 변수 선언
   private Map<String, Socket> clients;
   
   public MultiChatServer() {
      clients = Collections.synchronizedMap(new HashMap<String, Socket>());
   }
   
   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() + "] 에서 접속하였습니다.");
            
            // 사용자가 보내준 메시지를 받아서 처리하는 스레드 생성 및 실행
            ServerRecevier sr = new ServerRecevier(socket);
            sr.start();
         }
         
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }
   
   /**
    * 대화방, 즉 Map에 저장된 모든 접속한 사람들에게  안내메시지를 전송하기 위한 메서드 
    * @param msg 전송할 메시지
    */
   public void sendMessage(String msg){
	   Iterator<String> it = clients.keySet().iterator();
	   
	   while (it.hasNext()) {
		try {
			String name = it.next(); // 대화명
			 
			// 해당 사용자에게 메시지 보내기
			//getgetOutputStream 객체 만들어서 꺼냄
			new DataOutputStream(
					clients.get(name).getOutputStream()).writeUTF(msg);
			
		}  catch (IOException ex) {
	         ex.printStackTrace();
	      }
	}
	   
	   
   }
   //파라미터 2개라서 하나더 만듬 오버로딩
   /**
    * 대화방, 즉 Map에 저장된 모든 접속한 사람들에게  채팅 메시지 전송하기 위한 메서드 
    * @param msg 채팅메세지
    * @param from 메시지 보낸 대화명
    */
   public void sendMessage(String msg, String from){
	   sendMessage("[" +from + "]"+ msg);
   }
   
   
   
   /**
    * 서버에서 클라이언트로부터 수신한 메세지를 처리하기 위한 클래스
    * Inner클래스로 정의함.(Inner클래스에서는 부모클래스의 멤버들을 직접 접근할 수 있음)
    * @author PC-10
    *
    */
   class ServerRecevier extends Thread{
      private Socket socket;
      private DataInputStream dis;
      private String name;
      
      public ServerRecevier(Socket socket) {
         this.socket = socket;
         
         try {
            dis = new DataInputStream(socket.getInputStream());
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
      
      @Override
      public void run() {
         try {
            // 서버에서는 클라이언트가 보내온 최초의 메시지, 즉 대화명을 수신해야 한다.
            name = dis.readUTF();
            
            // 대화명을 받아서 다른 모든 클라이언트들에게 대화방 참여 메시지를 보낸다.
            sendMessage("#" + name + "님이 입장했습니다.");
            
            // 대화명을 사용하여 소켓객체를 Map에 저장한다.
            clients.put(name, socket);
            
            System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");

            // 이 이후의 처리는 반복문으로 처리한다.
            // 클라이언트가 보내온 메시지(대화내용)를 접속한 다른 모든 클라이언트
            
            while (dis != null) {
            	//name 앞에 누구인지 출력하기 위한용
            	sendMessage(dis.readUTF(),name);
			}
            
         } catch (IOException ex) {
            ex.printStackTrace();
         }finally {
			//이 부분이 실행된다는것은 클라이언트와 접속에 문제가 생겼다는 의미
        	// 그래서 접속종료 처리를 한다.
        	 sendMessage(name + "님이 나가셧습니다.");
        	 
        	 // Map에서 해당 사용자를 삭제한다.
        	 clients.remove(name);
        	 
        	 System.out.println("[" + socket.getInetAddress() + " : " 
                     + socket.getPort() + "] 에서 종료하였습니다.");
        	 
        	 System.out.println("현재 서버 접속자 수는 " + clients.size() + "명 입니다.");
		}
      }
   }
   public static void main(String[] args) {
		new MultiChatServer().serverStart();
	}
}

MultiChatClient

더보기
package kr.or.ddit.tcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class MultiChatClient {
	
	public void start() {
		//소켓접속
		Socket socket = null;
		try {
			socket = new Socket("192.168.144.~", 7777);
			System.out.println("멀티챗 서버에 접속했습니다.");
			
			//송신용 스레드 생성 및 실행
			ClientSender sender = new ClientSender(socket);
			sender.start();
			
			//수신용 스레드 생성 및 실행
			ClientReceiver receiver = new ClientReceiver(socket);
			receiver.start();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//메시지 전송을 위한 스레드 클래스
	class ClientSender extends Thread{
		private DataOutputStream dos;
		private Scanner scan;
		
		public ClientSender(Socket socket) {
			scan = new Scanner(System.in);
			try {
				dos = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			try {
				if(dos != null) { //대화명
					//시작하자마자 자신을 대화명을 서버로 전송하기
					System.out.println("대화명 >>");
					dos.writeUTF(scan.nextLine());
				}
				
				while (dos != null) {
					//이제부터는 일반 채팅메시지 전송하기
					dos.writeUTF(scan.nextLine());
				}
				
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//메시지 수신을 위한 스레드 클래스
	class ClientReceiver extends Thread{
		private DataInputStream dis;
		
		public ClientReceiver(Socket socket) {
			try {
				dis = new DataInputStream(socket.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			try {
				while (dis != null) {
					//서버로부터 받은 메시지 콘솔에 출력하기
					System.out.println(dis.readUTF());
				}
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
	}	
	public static void main(String[] args) {
		new MultiChatClient().start();
	}
}