몰입형
[몰입형] Multicast
다콩잉
2022. 9. 6. 16:37
Multicast
멀티캐스트(multicast)란 한 번의 송신으로 메시지나 정보를 목표한 여러 컴퓨터에 동시에 전송하는 것을 말한다.
다음은 자바 Swing을 이용하여 디자인 하고, 멀티캐스를 구현한 채팅 서비스이다.
기능
1. 로그인 - 닉네임 입력 확인, 특수문자 포함 불가능
2. 채팅창
- 귓속말
- 강퇴하기
- 채팅 입력 시간 표시
- 비속어 사용시 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