package ex03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
public class MultiChatServer {
private ServerSocket serverSocket = null;
private Socket socket = null;
ArrayList <ChatThread> chatlist = new ArrayList <ChatThread>();
HashMap<String, ChatThread> hash= new HashMap<String, ChatThread>();
public void start() {
try {
serverSocket = new ServerSocket(8888);
System.out.println("server start");
while(true) {
socket = serverSocket.accept();
ChatThread chat = new ChatThread();
chatlist.add(chat);
chat.start();
}
} catch(Exception e) {
System.out.println("[MultiChatServer]start() Exception 발생!!");
}
}
public static void main(String[] args) {
MultiChatServer server = new MultiChatServer();
server.start();
}
void broadCast(String msg) { //채팅방 인원 전체출력
for(ChatThread ct : chatlist) {
ct.outMsg.println(msg);
}
}
void wisper(ChatThread from, ChatThread to,String msg) { //송신그레드,수신스레드,대화내용 매개변수)
from.outMsg.println(msg); //송신스레드 채팅창에 출력
to.outMsg.println(msg); // 수신스레드 채팅창에 출력
}
class ChatThread extends Thread {
String msg;
String[] rmsg;
private BufferedReader inMsg = null;
private PrintWriter outMsg = null;
public void run() {
boolean status = true;
System.out.println("##ChatThread start...");
try {
inMsg = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outMsg = new PrintWriter(socket.getOutputStream(), true);
while(status) {
msg = inMsg.readLine();
rmsg = msg.split("/");
if(rmsg[1].equals("logout")) {
hash.remove(rmsg[0]); //hashmap에서 제거
chatlist.remove(this); //chatlist에서 제거
broadCast("SERVER/" + rmsg[0] + "님이 종료했습니다.");
status = false;
}
else if(rmsg[1].equals("login")) {
if(hash.containsKey(rmsg[0])) { //id로 hashmap에서 중복검사
this.outMsg.println("SERVER/ 로그인불가>ID 중복");
//로그인 한 상대방 채팅창에 로그인 불가 안내메시지 출력
socket.close(); //소켓 제거
chatlist.remove(this); //스레드리스트에서 제거
status = false; // 상태변경으로 while문 탈출
}
else{
hash.put(rmsg[0], this);
//중복이 아니면 해당 아이디를 key/ 스레드를 value로 추가
broadCast("SERVER/"+rmsg[0]+"님이 로그인했습니다.");
//채팅창에 로그인 메세지 출력
}
}
else if(hash.containsKey(rmsg[1])) {
// 귓속말을 보낼경우 rmsg[0]: 송신id / rmsg[1] 수신id / rmsg[3] 내용 이렇게 분할
// rmsg[1]이 hashmap에 있는 지 검사 있으면 귓속말의 경우로 판단
// 수정필요 다른 기능 구현할 때 문제가 될 가능성
ChatThread from = hash.get(rmsg[0]);
// rmsg[0] 송신 id를 key값으로 value스레드 찾기
ChatThread to = hash.get(rmsg[1]);
// rmsg[1] 수신 id를 key값으로 value스레드 찾기
wisper(from,to, msg);
//찾은 송신 스레드 , 수신 스레드, 내용을 매개변수로 wisper메소드 호출
}
else {
broadCast(msg);
}
}
this.interrupt();
System.out.println("##"+this.getName()+"stop!!");
} catch(IOException e) {
chatlist.remove(this);
// e.printStackTrace();
System.out.println("[ChatThread]run() IOException 발생!!");
}
}
}
}
클라이언트
package ex03;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class MultiChatClient implements ActionListener, Runnable {
private String ip;
private String id;
private String contents;
private Socket socket;
private BufferedReader inMsg = null;
private PrintWriter outMsg = null;
private JPanel loginPanel;
private JButton loginButton;
private JLabel label1;
private JTextField idInput;
private JPanel logoutPanel;
private JLabel label2;
private JButton logoutButton;
private JPanel msgPanel;
private JTextField msgInput;
private JButton exitButton;
private JFrame jframe;
private JTextArea msgOut;
private Container tab;
private CardLayout clayout;
private Thread thread;
boolean status;
public MultiChatClient(String ip) {
this.ip = ip;
loginPanel = new JPanel();
loginPanel.setLayout(new BorderLayout());
idInput = new JTextField(15);
loginButton = new JButton("로그인");
loginButton.addActionListener(this);
label1 = new JLabel("대화명");
loginPanel.add(label1, BorderLayout.WEST);
loginPanel.add(idInput, BorderLayout.CENTER);
loginPanel.add(loginButton, BorderLayout.EAST);
logoutPanel = new JPanel();
logoutPanel.setLayout(new BorderLayout());
label2 = new JLabel();
logoutButton = new JButton("로그아웃");
logoutButton.addActionListener(this);
logoutPanel.add(label2, BorderLayout.CENTER);
logoutPanel.add(logoutButton, BorderLayout.EAST);
msgPanel = new JPanel();
msgPanel.setLayout(new BorderLayout());
msgInput = new JTextField(30);
msgInput.addActionListener(this);
exitButton = new JButton("종료");
exitButton.addActionListener(this);
msgPanel.add(msgInput, BorderLayout.CENTER);
msgPanel.add(exitButton, BorderLayout.EAST);
tab = new JPanel();
clayout = new CardLayout();
tab.setLayout(clayout);
tab.add(loginPanel, "login");
tab.add(logoutPanel, "logout");
jframe = new JFrame("::멀티챗::");
msgOut = new JTextArea("", 10, 30);
msgOut.setEditable(false);
JScrollPane jsp = new JScrollPane
(msgOut, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
jframe.add(tab, BorderLayout.NORTH);
jframe.add(jsp, BorderLayout.CENTER);
jframe.add(msgPanel, BorderLayout.SOUTH);
clayout.show(tab, "login");
jframe.pack();
jframe.setResizable(false);
jframe.setVisible(true);
}
public void connectServer() {
try {
socket = new Socket(ip, 8888);
System.out.println("[Client]Server 연결 성공!!");
inMsg = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outMsg = new PrintWriter(socket.getOutputStream(), true);
outMsg.println(id+"/"+"login");
thread = new Thread(this);
thread.start();
} catch(Exception e) {
// e.printStackTrace();
System.out.println("[MultiChatClient]connectServer() Exception 발생!!");
}
}
public void actionPerformed(ActionEvent arg0) {
Object obj = arg0.getSource();
if(obj == exitButton) {
System.exit(0);
} else if(obj == loginButton) {
id = idInput.getText();
label2.setText("대화명 : " + id);
clayout.show(tab, "logout");
connectServer();
} else if(obj == logoutButton) {
outMsg.println(id + "/" + "logout");
msgOut.setText("");
clayout.show(tab, "login");
outMsg.close();
try {
inMsg.close();
socket.close();
} catch(IOException e) {
e.printStackTrace();
}
status = false;
} else if(obj == msgInput) {
contents = msgInput.getText(); //입력창의 내용 contents에 대입
if(contents.indexOf("to")!=-1) {
// contents에 to가 있다면 (to가 없으면 -1을 반환하는데,-1이 아니라면 )
int begin = contents.indexOf(" ") + 1;
// to 1111 안녕하세요 일 경우 처음 빈칸 다음자리부터
int end = contents.indexOf(" ", begin);
//끝자리 포함x(+1 안함) // 다음 빈칸까지(마지막 자리는 포함 안됨)
String toid = contents.substring(begin, end);
//contents에서 해당 부분을 찾아 toid에 대입
String wisper = contents.substring(end+1);
//두번째 빈칸 다음자리부터 끝까지를 뽑아서 wisper에 저장(내용)
outMsg.println(id + "/"+ toid+ "/" + wisper);
// 각 내용을 /로 구분해서 출력
}
else {outMsg.println(id + "/" + contents);
}
msgInput.setText("");
}
}
public void run() {
String msg;
String[] rmsg;
int size;
status = true;
while(status) {
try {
msg = inMsg.readLine();
rmsg = msg.split("/");
size = rmsg.length;
if(size>=3) {
// 입력창에 "to 귓속말상대방아이디 귓속말 내용 " 이렇게 입력할 경우
//size가 자동으로 3
// 수정해야 함 추후에 다른 기능 집어 넣을 때 구별이 안되는 문제 발생
msgOut.append(rmsg[0] + ">>"+rmsg[1] + "\n" + rmsg[2] +"\n");
}
else {
msgOut.append(rmsg[0] + ">"+rmsg[1] + "\n");
}
msgOut.setCaretPosition(msgOut.getDocument().getLength());
} catch(IOException e) {
// e.printStackTrace();
status = false;
}
}
System.out.println("[MultiChatClient]" + thread.getName() + "종료됨");
}
public static void main(String[] args) {
MultiChatClient mcc = new MultiChatClient("127.0.0.1");
}
}
귓속말 관련 코드에만 주석 넣음
일단 구현을 목적으로 한거라 수정해야 하는 요소 있음
나중에 방장의 강퇴 기능같은 걸 넣는다면 van 강퇴아이디 이렇게 구현할 생각인데 그러면 위 코드로는 구분이 안됨
chatlist / hash 두개나 필요한가 싶음
chatlist를 빼려면 broadcast 코드를 바꿔야 하는데 지금 당장 생각이 안 떠오른다.
동기화를 깜박했네... 멀티 스레드에서 가장 중요한 일인데...
아직 갈길이 멀다...
스레드 풀도 써야하고
각 기능별로 분리도 시켜야 하는데... 멀티스레드에 대한 이해가 덜 되서 자꾸 데드락이 발생한다.
안드로이드 앱까지 완성하는게 목표인데 과연 할 수 있을 지....
현재 구상중인 기능
도배방지
채팅내용 입력시간 같이 출력
방 참가자 리스트 구현 (gui로 따로 패널을 만들어야 할 것 같은데 gui 공부하기 싫은데,,,,)
방장 강퇴권한 부여
chatlist[0]인 사람한테 권한을 주려고 함
0번 사람이 나가면 다시 0번이 된 사람이 권한을 부여받고 안내메시지 출력
'JAVA' 카테고리의 다른 글
채팅 프로그램 ( 욕설필터 기능 구현) (1) | 2021.03.15 |
---|---|
JAVA 소켓 채팅 프로그램 강퇴기능 구현 + 업데이트 (1) | 2021.03.13 |
JAVA 소켓 채팅 프로그램 (server) (0) | 2021.03.04 |
JAVA 소켓 채팅 프로그램 (Client) (0) | 2021.03.04 |
은행관리 프로그램 JDBC 연동 java.sql.SQLException (0) | 2021.01.30 |