몰입형

[몰입형] Multicast

다콩잉 2022. 9. 6. 16:37

Multicast

멀티캐스트(multicast)한 번의 송신으로 메시지나 정보를 목표한 여러 컴퓨터에 동시에 전송하는 것을 말한다. 

다음은 자바 Swing을 이용하여 디자인 하고, 멀티캐스를 구현한 채팅 서비스이다.

 

기능

1. 로그인 - 닉네임 입력 확인, 특수문자 포함 불가능

2. 채팅창

- 귓속말

- 강퇴하기

- 채팅 입력 시간 표시

-  비속어 사용시 5초간 채팅 금지

- 비속어 필터링

- 채팅방 색상(다크모드, 라이트모드)

닉네임을 아무것도 입력하지 않고 로그인을 시도하면 알림창이 뜸

 

특수문자가 포함된 닉네임은 사용할 수 없음

 

사용자 목록 클릭시 귓속말이나 강퇴를 할 수 있음

 

자기 자신에게 귓속말을 보내거나 강퇴할 수 없음

 

귓속말을 보낸 상대에게만 메세지가 보임

 

강퇴하기를 누를 경우 알림창이 뜨고 예를 누르면

 

추방당했다는 메세지와 함께 aaa는 사용자 목록에서 제외됨

 

다크모드와 라이트모드를 선택할 수 있음
비속어 입력
경고 알림창
5초간 채팅을 보낼 수 없으며 비속어는 필터링되어 표시

package Multicast;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import java.net.*;
class ReceiverThread extends Thread {
    Socket socket;
    SenderThread send;
    String name;
    ReceiverThread(Socket socket, SenderThread send, String name) {
        this.socket = socket;
        this.send = send;
        this.name = name;
    }
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                Date now = new Date(System.currentTimeMillis());
                SimpleDateFormat simple= new SimpleDateFormat("(a hh:mm)");

      //서버로부터 수신된 메시지를 모니터로 출력
                String str = reader.readLine();
                if (str == null) 
                   break;
                if(str.contains("#")) {
                   String newStr = str.replace("#", "");
                   send.area.append(newStr+simple.format(now));
                   send.area.append("\n");
                   if(str.contains("님이 나가셨습니다")) {
                      String[] strArr = str.split("#");
                      send.arr.removeElement(strArr[1]);
                   }else if(str.contains("#kick")) {
                      String[] strArr = str.split("#");
                      if(name.equals(strArr[2])) {
                         send.sendMsg("#redCard");
                         System.exit(0); 
                      }
                   }else if(str.contains("님이 추방당하셧습니다")) {
                      String[] strArr = str.split("#");
                      send.arr.removeElement(strArr[1]);
                   }
                }else if(str.contains("->")) {   // 귓속말
                    String[] whisper = str.split("->");
                    if(whisper[1].equals(name)) {
                       send.area.append(whisper[0].replace(">", "(귓)>") + whisper[2]);
                       send.area.append("\n");
                    }else if(whisper[0].equals(name+">")) {
                       send.area.append(whisper[1]+"(에게)>" + whisper[2]);
                       send.area.append("\n");
                    }
                 }else if(!str.contains(">")) {
                   if(str.contains("&")) {
                      String[] strArr = str.split("&");
                      System.out.println(strArr.length);
                      send.arr.clear();
                      for(int i = 0; i < strArr.length; i++) {
                         if(strArr[i].equals(name)) {
                            send.arr.addElement(strArr[i] + "(나)");
                         }else {
                            send.arr.addElement(strArr[i]);
                         }
                      }
                   }
                }else {
                   String[] strArr = str.split(">");
                   if(strArr[0].equals(name)) {
                      str = strArr[0] + "(나)>" + strArr[1];
                      send.area.append(str);
                        send.area.append("\n");
                   }else {
                	send.area.append(str);
                    send.area.append("\n");}
                }
                System.out.println(str+simple.format(now));
            }
        }
        catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}​

 

package Multicast;

import java.net.*;
class ServerExample4 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(9002);
            while (true) {
                Socket socket = serverSocket.accept();
                Thread thread = new PerClinetThread(socket);
                thread.start();
            }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
package Multicast;
//각 클라이언트 접속에 대해 하나씩 작동하는 스레드 클래스 
import java.io.*;
import java.net.*;
import java.util.*;
class PerClinetThread extends Thread {
 
  // ArrayList 객체를 여러 스레드가 안전하게 공유할 수 있는 동기화된 리스트로 만듭니다.
  static List<PrintWriter> list = Collections.synchronizedList(new ArrayList<PrintWriter>());
  static ArrayList<String> nameList = new ArrayList<String>();
  Socket socket;
  PrintWriter writer;
  boolean kicked=false;
  PerClinetThread(Socket socket) {
      this.socket= socket;
      try {
          writer = new PrintWriter(socket.getOutputStream());
          list.add(writer);
      }
      catch (Exception e) {
          System.out.println(e.getMessage());
      }
  }
  public void run() {
      String name = null;
      try {
          BufferedReader reader = new BufferedReader(
              new InputStreamReader(socket.getInputStream()));
     
          // 수신된 첫번째 문자열을 대화명으로 사용하기 위해 저장
          name = reader.readLine(); 
          nameList.add(name);
          
          sendAll("#" + name + "#님이 들어오셨습니다");
          while (true) {
             String nameStr = "";
              for(int i = 0; i < nameList.size(); i++) {
                 nameStr += nameList.get(i) + "&";
              }
              sendAll(nameStr);
              String str = reader.readLine();
              if (str == null)
                  break;
              if (str.contains("#kick")) {
                 String[] strArr = str.split("#");
                 sendAll("#kick"+name+"님이#" +strArr[2]+"#님을 추방하였습니다");
              }else if (str.contains("#redCard")) {   
                 kicked=true;
              }else {
            	  sendAll(name + ">" + str);  // 수신된 메시지 앞에 대화명을 붙여서 모든 클라이언트로 송신}
              }
          }
      }
      catch (Exception e) {
          System.out.println(e.getMessage());
      }
      finally {
         if(kicked == true) {
              list.remove(writer);
              nameList.remove(name);
              sendAll("#" + name + "#님이 추방당하셧습니다"); // 사용자가 채팅을 종료했다는 메시지를 모든 클라이언트로 보냅니다.
              try {
                  socket.close();
              }
              catch (Exception ignored) {
              }
         }else {
              list.remove(writer);
              nameList.remove(name);
              sendAll("#" + name + "#님이 나가셨습니다"); // 사용자가 채팅을 종료했다는 메시지를 모든 클라이언트로 보냅니다.
              try {
                  socket.close();
              }
              catch (Exception ignored) {
              }
         }
      }
  }
 
  // 서버에 연결되어 있는 모든 클라이언트로 똑같은 메시지를 보냅니다.
  private void sendAll(String str) {  
      for (PrintWriter writer : list) {
          writer.println(str);
          writer.flush();
      }
  }
}
package Multicast;

import java.net.*;
class ClientExample4 {
    public static void start(String name) {
    	try {
    	    // 서버와 연결
                Socket socket = new Socket("127.0.0.1", 9002);
                 // 메시지 송신 쓰레드와 수신 쓰레드 생성해서 시작
                SenderThread thread1 = new SenderThread(socket, name);
                Thread thread2 = new ReceiverThread(socket, thread1, name); 
                
                thread1.start();
                thread2.start();
            }
            catch (Exception e) {
                System.out.println(e.getMessage());
            }
    }
}
package Multicast;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class Login {
	static JTextField text;
	static JFrame frame;
	public static void login() {
    	frame = new JFrame("Login");
    	frame.setBounds(400, 300, 400, 240);
    	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	frame.setLayout(new BorderLayout());// 동,서,남,븍 나뉨
    	frame.setResizable(false); 	// 크기 고정
    	
    	JPanel panel = new JPanel();
    	panel.setLayout(null);
    	
    	JLabel la = new JLabel("닉네임");
    	la.setBounds(65, 70, 80, 35);	// x, y, width, height
    	panel.add(la);
    	
    	text = new JTextField();
    	text.setBounds(125, 73, 160, 30);
    	text.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
               if(e.getKeyCode()==KeyEvent.VK_ENTER) {//엔터키 누를시
                  action();
               }
            }
         });
    	panel.add(text);
    	
    	JButton btn = new JButton("로그인");
    	btn.setBackground(Color.lightGray);
    	btn.setBounds(125, 110, 160, 30);
    	Font font = new Font("맑은 고딕", Font.BOLD, 10);
    	btn.setFont(font);
    	btn.addActionListener(new ActionListener() {
    		@Override
    		public void actionPerformed(ActionEvent e) {
    			if(e.getActionCommand().equals("로그인")) {
    				action();
    			}
    		}
    	});
    	
    	panel.add(btn);
    	
    	frame.setContentPane(panel);
    	frame.setVisible(true);
    }
	
	public static void action() {
		String[] chars = new String[] {"~", "`", "!", "@", "#", "$", "%", "^",
				  "&", "*", "(", ")", "-", "_", "+",
				  "=", "'", "<", ">", "?", "/", ";", ":", "|"};
		String name = text.getText();
		if(name.length() < 1) {
			JOptionPane.showMessageDialog(null,"닉네임을 입력해주세요");
		}else{
			int cnt = 0;
			for(int i = 0; i < chars.length; i++) {
				if(name.contains(chars[i])) {
					cnt++;
				}
			}
			if(cnt > 0) {
				JOptionPane.showMessageDialog(null,"특수문자는 포함하실 수 없습니다");
			}else {
				frame.setVisible(false);
				ClientExample4.start(name);
			}
		}
	}
	
	
    public static void main(String[] args) {
        if (args.length != 1) {
        	login();
        }
    }
}
package Multicast;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import java.net.*;
class ReceiverThread extends Thread {
    Socket socket;
    SenderThread send;
    String name;
    ReceiverThread(Socket socket, SenderThread send, String name) {
        this.socket = socket;
        this.send = send;
        this.name = name;
    }
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                Date now = new Date(System.currentTimeMillis());
                SimpleDateFormat simple= new SimpleDateFormat("(a hh:mm)");

      //서버로부터 수신된 메시지를 모니터로 출력
                String str = reader.readLine();
                if (str == null) 
                   break;
                if(str.contains("#")) {
                   String newStr = str.replace("#", "");
                   send.area.append(newStr+simple.format(now));
                   send.area.append("\n");
                   if(str.contains("님이 나가셨습니다")) {
                      String[] strArr = str.split("#");
                      send.arr.removeElement(strArr[1]);
                   }else if(str.contains("#kick")) {
                      String[] strArr = str.split("#");
                      if(name.equals(strArr[2])) {
                         send.sendMsg("#redCard");
                         System.exit(0); 
                      }
                   }else if(str.contains("님이 추방당하셧습니다")) {
                      String[] strArr = str.split("#");
                      send.arr.removeElement(strArr[1]);
                   }
                }else if(str.contains("->")) {   // 귓속말
                    String[] whisper = str.split("->");
                    if(whisper[1].equals(name)) {
                       send.area.append(whisper[0].replace(">", "(귓)>") + whisper[2]);
                       send.area.append("\n");
                    }else if(whisper[0].equals(name+">")) {
                       send.area.append(whisper[1]+"(에게)>" + whisper[2]);
                       send.area.append("\n");
                    }
                 }else if(!str.contains(">")) {
                   if(str.contains("&")) {
                      String[] strArr = str.split("&");
                      System.out.println(strArr.length);
                      send.arr.clear();
                      for(int i = 0; i < strArr.length; i++) {
                         if(strArr[i].equals(name)) {
                            send.arr.addElement(strArr[i] + "(나)");
                         }else {
                            send.arr.addElement(strArr[i]);
                         }
                      }
                   }
                }else {
                   String[] strArr = str.split(">");
                   if(strArr[0].equals(name)) {
                      str = strArr[0] + "(나)>" + strArr[1];
                      send.area.append(str);
                        send.area.append("\n");
                   }else {
                	send.area.append(str);
                    send.area.append("\n");}
                }
                System.out.println(str+simple.format(now));
            }
        }
        catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}
package Multicast;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;


import java.util.ArrayList;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
class SenderThread extends Thread implements ActionListener{
    Socket socket;
    String name, whispering, my;
    PrintWriter writer;
    JTextField tf;
    JTextArea area = new JTextArea();
    DefaultListModel<String> arr = new DefaultListModel<>();
    JList list;
    JLabel peopleList, label;
    JFrame frame;
    String str = "";
    JButton btn3;
    SimpleDateFormat simple;
    Date now;
    JPanel panel;
    //JButton whbtn;
    SenderThread(){}
    
    SenderThread(Socket socket, String name) { 
        this.socket = socket;
        this.name = name;
        
        frame = new JFrame("Chat");
        frame.setBounds(400, 140, 600, 540);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);    // 크기 고정
       
        panel = new JPanel();
        panel.setLayout(null);
       
        // 채팅
        JScrollPane scrollPane = new JScrollPane(area);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);   // 스크롤바 항상 보이게
        scrollPane.setBorder(null);
        scrollPane.setBounds(10, 10, 360, 440);
        panel.add(scrollPane);
        area.setEditable(false);
      
       // 사용자 목록
        label = new JLabel("사용자 목록");
        label.setBounds(440, 10, 80, 20);
        panel.add(label);
       
        list = new JList(arr);
        //list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
       
        list.addMouseListener(new MouseAdapter() {
           public void mouseClicked(MouseEvent e) {
              if(list.getSelectedValue() !=null) {
                System.out.println("d"+list.getSelectedValue());
                  JPopupMenu menu = new JPopupMenu();
                  JMenuItem whbtn  = new JMenuItem("귓속말");
                  whbtn.addActionListener(new ActionListener() {
                         public void actionPerformed(ActionEvent e) {
                            peopleList.setText(whispering + "에게");
                            tf.requestFocus();
                         }
                     });                  
                  
                  JMenuItem kick = new JMenuItem("강퇴하기");
                  kick.addActionListener(new ActionListener() {
                         public void actionPerformed(ActionEvent e) {
                           int result=JOptionPane.showConfirmDialog(null,"진짜로 강퇴하시겠습니까?","강퇴확인",JOptionPane.YES_NO_OPTION);
                          if(result==JOptionPane.YES_OPTION) {
                               String kickCommand = "#kick#"+whispering;
                              writer.println(kickCommand);
                              writer.flush();
                          }else {
                             JOptionPane.showMessageDialog(null,"취소하였습니다");
                          }
                           
                         }
                     });   
                  
                  menu.addSeparator();
                     menu.add(whbtn);
                     menu.add(kick);
                     menu.show(e.getComponent(),e.getX(),e.getY());
              }
           }
       });
        list.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
               whispering = (String) list.getSelectedValue();
               if(whispering != null) {
                  // 자기 자신에게는 귓속말하지 못하도록함
                  if(whispering.equals(name+"(나)")) {
                     list.clearSelection();
                     whispering = null;
                     
                  }else {

                  }
               }
            }
             
          });
       
       JScrollPane scrollPane2 = new JScrollPane(list);
       scrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
       scrollPane2.setBorder(null);
       scrollPane2.setBounds(380, 35, 190, 385);
      panel.add(scrollPane2);
       peopleList = new JLabel("모두에게");
       peopleList.setBounds(10, 464, 50, 20);
       panel.add(peopleList);
       
       tf = new JTextField();
       tf.setBounds(70, 460, 200, 30);
       panel.add(tf);
       tf.addKeyListener(new KeyAdapter() {
         public void keyPressed(KeyEvent e) {
        	 	
               now = new Date(System.currentTimeMillis());
               simple= new SimpleDateFormat("(a hh:mm)");
            if(e.getKeyCode()==KeyEvent.VK_ENTER) {//엔터키 누를시
            	action(btn3);
            }
         }
      });
       
      JButton btn1 = new JButton("다크모드");
      btn1.setBounds(380, 425, 190, 30);
      btn1.setBackground(Color.lightGray);
      btn1.addActionListener(this);
       panel.add(btn1);
       
      btn3 = new JButton("보내기");
      btn3.setBounds(270, 460, 95, 30);
      btn3.setBackground(Color.lightGray);
      btn3.addActionListener(this);
      panel.add(btn3);
       
       JButton btn2 = new JButton("나가기");
      btn2.setBounds(380, 460, 190, 30);
      btn2.setBackground(Color.lightGray);
      btn2.addActionListener(this);
       panel.add(btn2);
       
       frame.setContentPane(panel);
       frame.setVisible(true);
    }
    public void run() {
       try {
            //BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            writer = new PrintWriter(socket.getOutputStream());
            
          // 제일 먼저 서버로 대화명 송신한다.
          writer.println(name);
           writer.flush();
           
           while (true) {
               if (str.equals("bye"))
                   break;
           }
        }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
        finally {
            try { 
                socket.close(); 
            } 
            catch (Exception ignored) {
            }
        }
    }
    
    public void action(JButton button) {
    	Timer timer = new Timer(5000, new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
            	tf.setEnabled(true);
            	tf.requestFocus();
                button.setEnabled(true);
                button.setText("보내기");
            }
        });
    	timer.setRepeats(false);
    	String chat = tf.getText();
        if(chat.length() > 0) {
        	String[] chars = new String[] {"시발", "병신", "개새끼", "바보", "멍청이", "존나"};
        	int cnt = 0;
        	for(int i = 0; i < chars.length; i++) {
        		if(chat.contains(chars[i])) {
        			chat = chat.replace(chars[i], "*".repeat(chars[i].length()));
        			cnt++;
        		}
        	}
        	if(cnt > 0) {
        		tf.setEnabled(false);
        		button.setEnabled(false);
            	button.setText("채금");
            	timer.start();
            	JOptionPane.showMessageDialog(null,"비속어를 사용하셨습니다. 5초간 채팅 금지입니다.");
        	}
        	if(peopleList.getText().equals("모두에게")) {   // 귓속말 보내는중
                String[] to = peopleList.getText().split("에게");
                writer.println(chat+simple.format(now));
                tf.setText("");
                 writer.flush();
             }else {
                String[] to = peopleList.getText().split("에게");
                writer.println("->" + to[0] + "->" + chat+simple.format(now));
                tf.setText("");
                writer.flush();
                peopleList.setText("모두에게");
             }
        }
    }
    

   @Override
   public void actionPerformed(ActionEvent e) {
         now = new Date(System.currentTimeMillis());
         simple = new SimpleDateFormat("(a hh:mm)");
      if(e.getActionCommand().equals("보내기")) {
    	 JButton button = (JButton)e.getSource();
         action(button);
      }else if(e.getActionCommand().equals("다크모드")) {
          JButton btn = (JButton)e.getSource();
          area.setBackground(Color.DARK_GRAY);
          area.setForeground(Color.WHITE);
          list.setBackground(Color.DARK_GRAY);
          list.setForeground(Color.WHITE);
          panel.setBackground(Color.GRAY);
          tf.setBackground(Color.DARK_GRAY);
          tf.setForeground(Color.WHITE);
          label.setForeground(Color.white);
          peopleList.setForeground(Color.white);
          btn.setText("라이트 모드");
          
          }else if(e.getActionCommand().equals("라이트 모드")) {
             JButton btn = (JButton)e.getSource();
             area.setBackground(Color.WHITE);
             area.setForeground(Color.BLACK);
             list.setBackground(Color.WHITE);
             list.setForeground(Color.BLACK);
             panel.setBackground(new Color(238, 238, 238));
             tf.setBackground(Color.WHITE);
             tf.setForeground(Color.BLACK);
             label.setForeground(Color.BLACK);
             peopleList.setForeground(Color.BLACK);
             btn.setText("다크모드");      
          }else if(e.getActionCommand().equals("나가기")) {
         //str = "bye";
         frame.dispose();
         System.exit(0);
      }
   }
   void sendMsg(String msg) {
      writer.println(msg);
      writer.flush();
   }
   
}

 

728x90