Java/Chat

[자바] 자바 GUI 채팅 프로그램

달의요정루나 2021. 7. 2. 22:25

자바 채팅 프로그램을 만들려고 여러 시행착오를 거쳐왔는데

이제 완성이 되었네요 ㅠㅠ.

여러 웹서핑으로 만들어진 채팅 프로그램입니다!!!

package com.aaa.client;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.PrintWriter;

import javax.swing.JButton;
import javax.swing.JPanel;

public class Clientgui extends Frame implements ActionListener{
	private TextArea chatviewer; //채팅 대화창 만들기
	
	private TextField tfname; //이름 입력칸 만들기
	private TextField tfport; //포트번호 입력칸 만들기
	private TextField tfip; //ip 입력칸 만들기
	private JButton login; //로그인버튼으로 서버와 연결한다.
	
	private TextField tfinput; //대화입력칸 만들기
	private JButton tfenter; //엔터 버튼을 올려 대화내용을 공유한다.
	
	private PrintWriter pw; //데이터를 서버로 보내는 스트림이다.
	private String idname; //tfname에서 아이디를 가져올때 사용한다.
	private String serverip; //tfip에서 ip를 가져올때 사용한다.
	private int portnum = 10000;//지정된 포트번호
	
	public Clientgui() {
		//패널선언
		JPanel up = new JPanel();
		JPanel center = new JPanel();
		JPanel down = new JPanel();
		
		//up
		tfname = new TextField("이름",5);// 이름입력칸구성하기
		tfname.setText("이름 입력");
		tfname.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				tfname.setText("");
			}
		});
		
		tfport = new TextField(Integer.toString(portnum),5);// 포트번호 입력칸 구성하기
		tfport.setText("10000");
		
		tfip = new TextField("127.0.0.1",5);//ip입력칸 구성하기.
		tfip.setText("127.0.0.1");
		
		login = new JButton("시작"); //로그인 버튼 구성하기
		login.setPreferredSize(new Dimension(67,23));
		login.addActionListener(this);
		
		//center
		chatviewer = new TextArea(""); //채팅창 구성하기
		chatviewer.setPreferredSize(new Dimension(278,285));
		chatviewer.setEnabled(false);
		
		//down
		tfinput = new TextField(""); //입력창 구성하기
		tfinput.setPreferredSize(new Dimension(210,25));
		tfinput.addActionListener(this);
		
		tfenter = new JButton("엔터");//엔터 버튼 구성하기
		tfenter.setPreferredSize(new Dimension(60,25));
		tfenter.addActionListener(this);
		
		// 해당되는 패널에 각 요소들을 넣어준다.
		up.add(tfname);
		up.add(tfport);
		up.add(tfip);
		up.add(login);
		
		center.add(chatviewer);
		
		down.add(tfinput);
		down.add(tfenter);
		
		//Borderlayout을 이용해 위치를 지정한다.
		add(up,BorderLayout.NORTH);
		add(center,BorderLayout.CENTER);
		add(down, BorderLayout.SOUTH);
		
		//프레임 크기 설정
		setSize(300,400);
		setVisible(true);
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		//Event 객체를 반환한다.
		Object obj = e.getSource();
		if (obj == login) {
			// 로그인 버튼을 누르면 init()의 실행을 통해 Event가 서버와 연결한다.
			idname = tfname.getText();
			serverip = tfip.getText();
			portnum = Integer.parseInt(tfport.getText());
			
			init();
			
			chatviewer.setText("채팅 시작\n");
			
			login.setEnabled(false);
		} else if (obj == tfinput || obj == tfenter) {
			// Event가 textfield의 문장을 엔터버튼이나 
			//키보드의 엔터키를 통해 받으면 서버와 연결된다.
			String msg = tfinput.getText();
			
			//채팅내용을 다음 코드문에 따라 가공해서 전송한다.
			if (!msg.trim().equals("")) {
				try {
					String str = "contents"+":"+ idname + ":" + msg;
					pw.println(str);
					pw.flush();
					
					tfinput.setText("");
					tfinput.setFocusable(true);
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			} else {
				System.out.println("Error");
			}
		}
	}
	
	//Clientgui의 네트워크를 구현한다.
	public void init() {
		ClientNetwork clientNetwork = new ClientNetwork(this,serverip,portnum); 
		//클라이언트 네트워크 클래스 선언
		Thread thread = new Thread(clientNetwork);
		thread.start();
		
		try {
			//Thread 실행후 Socket에 대한 출력 스트림 객체를 반환하여 멤버변수에 저장한다.
			//다음 출력스트림은 서버로 채팅메시지 전송에 사용된다.
			pw = clientNetwork.getPw();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Clientgui clientgui = new Clientgui();
		clientgui.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				//채팅창을 나가면 다음 메시지가 나간다.
				String str = "end"+":"+clientgui.getIdname();
				clientgui.getPw().println(str);
				clientgui.getPw().flush();
				System.exit(0);				
			}
		});
	}
	
	public PrintWriter getPw() {
		return pw;
	}
	public TextArea getChatviewer() {
		return chatviewer;
	}
	public String getIdname() {
		return idname;
	}
	
	//ClientNetwork 클래스에서 쓰일 메소드이다.
	// 채팅창에 메시지 전달을 담당한다.
	public void appendMsg(String msg) {
		chatviewer.append(msg);
	}
	

}

1. Clientgui(클라이언트 gui프로그램)

package com.aaa.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public class ClientNetwork extends Thread {
	//채팅 clientgui 객체이다.
	private Clientgui clientgui;
	
	//서버와 연결된 socket을 담당한다.
	private Socket socket;
	
	private BufferedReader br;
	private PrintWriter pw;
	
	private String id;
	
	//serversocket과 연결된 socket을 멤버변수에 저장하고 출력스트림 객체를 생성한다.
	public ClientNetwork(Clientgui clientgui, String ip, int port){
		this.clientgui=clientgui;
		
		id=clientgui.getIdname();
		
		try {
			socket = new Socket(ip,port);
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//Thread 실행을 위한 run함수 이다.
	@Override
	public void run() {
		String message = null;
		try {
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			
			//서버에 접속한 후 채팅입장여부를 확인하는 문장을 전송한다.
			message = "id"+":"+id;
			pw.println(message);
			pw.flush();
			
			//무한루프를 돌며 서버로부터 수신되는 메시지를 표시한다.
			//만약 socket와의 연결이 끊어지면 thread가 정지된다.
			while (true) {
				message=br.readLine();
				clientgui.getChatviewer().append(message+"\n");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public PrintWriter getPw() {
		return pw;
	}
	public void setPw(PrintWriter pw) {
		this.pw = pw;
	}
}

2. ClientNetwork(Clientgui 네트워크 프로그램)

package com.aaa.server;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Servergui extends JFrame implements ActionListener{
	//채팅창을 선언한다.
	private TextArea chats;
	//String은 id가 들어가고 printwriter는 출력 스트림 객체를 저장한다.
	private Map<String, PrintWriter> clientMap;
	// 포트번호 선언
	private int portnum = 10000;
	//생성자를 선언한다.
	public Servergui() {
		JPanel panel = new JPanel();
		FlowLayout flowLayout = new FlowLayout();
		
		//채팅상환을 표시하는 textarea이다.
		chats = new TextArea();
		chats.setPreferredSize(new Dimension(450,450));
		chats.setEditable(false);
		
		panel.setLayout(flowLayout);
		panel.add(chats);
		
		add(panel,BorderLayout.CENTER);
		
		setSize(500,500);
		setVisible(true);
	}
	
	// 동작하는 동안 포트번호를 받아들이고 접속했다는 메시지를 출력한다.
	public void init() {
		ServerSocket serverSocket = null;
		try {
			//연결된 클라이언트를 저장할 Map<client_id, output_stream>를 생성한다. 생성 후 동기화를 설정한다.
			serverSocket = new ServerSocket(portnum);
			clientMap = new HashMap<String, PrintWriter>();
			Collections.synchronizedMap(clientMap);
			
			//serversocket에 연결이 이루어지면 해당 socket를 반환하고
			//채팅서버를 담당하는 ServerNetwork객체의 생성자에 매개변수를 넘겨 객체를 생성한다.
			while (true) {
				chats.append("연결중\n");
				Socket socket = serverSocket.accept();
				ServerNetwork serverNetwork = new ServerNetwork(clientMap, this, socket);
				Thread thread = new Thread(serverNetwork);
				thread.start();
			}
					
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	
	public static void main(String[] args) {
		Servergui servergui = new Servergui();
		servergui.init();
	}
	@Override
	public void actionPerformed(ActionEvent e) {}
	
	public void appendMsg(String msg) {
		chats.append(msg);
	}
	public TextArea getChats() {
		return chats;
	}
	public void setChats(TextArea chats) {
		this.chats = chats;
	}
	
	
}

3. Servergui(서버 gui 프로그램)

package com.aaa.server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;


public class ServerNetwork extends Thread{
	//포트번호가 들러올 수 있는 소켓과 
	//데이터가 다녀갈 수 있는 input과 output, Severgui클래스를 선언한다.
	//채팅서버의 gui를 담당하는 Servergui 객체와 
	//채팅클라이언트를 저장하는 Map<클라이언트ID, 출력스트림객체> 객체를 지정한다.
	private Map<String, PrintWriter> clientMap;
	private Servergui servergui;
	private Socket socket;
	
	private BufferedReader br;
	private String id;
	
	private boolean condition=true;
	
	public ServerNetwork(Map<String, PrintWriter>clientMap, Servergui servergui, Socket socket) {
		this.clientMap=clientMap;
		this.servergui=servergui;
		this.socket=socket;
	}
	
	//Thread 실행을 위한 run함수
	@Override
	public void run() {
		String message = null;
		String receivedMsg[] = null;
		
		try {
			//ServerSocket에서 반환되어 온 Socket 객체에서 입출력 스트림 객체를 불러온다.
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
			
			//receivedMsg[0]	receivedMsg[1]		receivedMsg[2]
			//id				<채팅 아이디>							//채팅방에서 입장할 때 전달된다.
			//contents			<채팅 아이디>		<채팅 내용>			//채팅방에서 진행되는 채팅내용이 전달된다.
			//end				<채팅 아이디>							//채팅방에서 탈퇴시 전달된다.
			while (condition) {
				message = br.readLine();
				receivedMsg = message.split(":");
				if (receivedMsg[0].equals("id")) {
					//채팅 클라이언트를 저장하는 Map 객체에 id와 출력 스티림 객체를 저장한다.
					clientMap.put(receivedMsg[1], pw);
					//servergui로 출력한다.
					servergui.getChats().append(receivedMsg[1]+" 님이 연결되었습니다.\n");
					//다른 클라이언트로 메시지를 발송한다.
					broadcast(receivedMsg[1] + " 님이 연결되었습니다.");
				} else if (receivedMsg[0].equals("contents")) {
					//servergui로 출력한다.
					servergui.getChats().append("["+receivedMsg[1]+"]"+": "+receivedMsg[2]+"\n");
					//다른 클라이언트로 메시지를 발송한다.
					broadcast("["+receivedMsg[1]+"]"+": "+receivedMsg[2]);
				} else if(receivedMsg[0].equals("end")){
					//채팅 클라이언트를 저장하는 Map 객체에 id와 출력 스티림 객체를 삭제한다.
					clientMap.remove(receivedMsg[1]);
					//servergui로 출력한다.
					servergui.getChats().append(receivedMsg[1]+" 님이 퇴장하셨습니다."+"\n");
					//다른 클라이언트로 메시지를 발송한다.
					broadcast(receivedMsg[1]+" 님이 퇴장하셨습니다."+"\n");
					//조건을 false로 바꾸어 while을 종료한다.
					condition = false;
				} else {
					System.out.println("ServerNetwork: Error");
				}
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//보유하고 있는 모든  Map객체로 메시지를 발송한다.
	private void broadcast(String msg) {
		Collection<PrintWriter> collection = clientMap.values();
		Iterator<PrintWriter> iterator = collection.iterator();
		
		while (iterator.hasNext()) {
			PrintWriter pw = iterator.next();
			System.out.println(msg);
			pw.println(msg);
			pw.flush();
		}
	}

}

4. ServerNetwork(서버 gui 네트워크 프로그램)

Severgui

 

Clientgui
Client A와 B유저가 접속
시작버튼을 누르면 버튼이 disable됨.
유저 A와 B가 접속했을 때의 서버 모습
유저 A가 B와 대화 중
유저 B가 A와 대화중
유저 A와 B가 대화할 떄 표시되는 서버의 모습
유저 A가 나가면 유저 B의 Clientgui와 Servergui에 위와 같이 표시된다.

현재 방학 중이네요...

빨리 코로나가 끝나서 대면으로 동기들도 만나고 싶네요

이번 여름 잘 지내세요!

'Java > Chat' 카테고리의 다른 글

[자바] 콘솔창을 이용한 자바채팅프로그램  (0) 2021.07.02
[자바] 에코서버 만들기  (0) 2021.07.02