Dev/Java

[Java] socket서버 실시간 채팅 구현 ( jdk-11.0.12.7-hotspot )

surimi🍥 2021. 12. 13. 23:33
반응형

1. 서버 ( ChatServer.java )

더보기
ChatServer.java
0.00MB
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ChatServer {

	private ServerSocket svSocket;
	private List<ChatHandler> clist;

	public ChatServer() {

		try {
			svSocket = new ServerSocket(9500); // 포트
			System.out.println("# 서버 준비 완료");
			
			clist = new ArrayList<ChatHandler>();

			while (true) {
				System.out.println("소켓 생성");
				
				Socket socket = svSocket.accept();
				
				ChatHandler handler = new ChatHandler(socket, clist);
				handler.start();
				
				clist.add(handler);
				System.out.println("생성된 리스트 개수 : " + clist.size());
				
				// handler 객체 안에서 스레드를 돌린다.
			
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		new ChatServer();
	}
}

 

생성자 Chatserver - 9500포트로 서버를 열고 들어오는 접속을 받아 각각 핸들러 객체 할당.

main - 서버 실행


2. 핸들러 ( ChatHandler.java )

더보기
ChatHandler.java
0.00MB
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.List;

public class ChatHandler extends Thread {

	private Socket socket;
	private List<ChatHandler> list;

	private BufferedReader br;
	private PrintWriter pw;
	
	public ChatHandler(Socket socket, List<ChatHandler> list) {

		this.socket = socket;
		this.list = list;

		try {
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));

		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	@Override
	public void run() {

		String line;
		
		// 닉네임 받는 쪽
		try {
			
			String nickName = br.readLine();

			// 모든 클라이언트에게 보내기
			broadCast(nickName + "님이 입장했습니다.");

			while(true) {
				
				line = br.readLine(); 

				// 종료
				if(line == null || line.toLowerCase().equals("quit")) {
					break;
				
				}

				// 별찍기
				if(line.toLowerCase().equals("star")) {
					
					for (int i = 1; i <= 5; i++) {

						String star = new String();

						//공백을 담당하는 j for 문
						for (int j = 5; j > i; j--) {

							star += " ";

						}

						//별을 담당하는 k for 문
						//5번의 별의 갯수는 2 * i - 1개 만큼의 별이 각 줄에 들어가게 된다.
						for (int k = 0; k < i * 2 - 1; k++) {

							star += "*";

						}

						broadCast(star);

					}
				
				}
				
				//메세지를 모든 클라이언트에게 보내기
				broadCast("[" + nickName + "]" + line);
				
			}//while
			
			// quit을 보낸 클라이언트에게 quit을 보내기
			pw.println("quit");
			pw.flush();
			
			// 남을 클라이언트에게 퇴장메세지 보내기
			list.remove(this); // 현재 handler를 list에서 지움
			broadCast(nickName + "님이 퇴장하였습니다.");
			
			br.close();
			pw.close();
			socket.close();
			

		} catch (IOException e) {
			e.printStackTrace();
		}

	}// run()

	public void broadCast(String msg) {

		for (ChatHandler handler : list) {
			
			handler.pw.println(msg); 
			handler.pw.flush();
			
		}
	}
}

생성자 ChatHandler - 소켓 객체와 전체 핸들러 리스트(모든 접속자 정보), 입출력 객체 생성

run - 접속 직후 닉네임을 입력받음. 메세지 또는 "quit", "star" 등으로 상호작용.

broadcast(String msg) - 모든 핸들러(접속자)에게 메세지를 전송 후 스트림 비움.


3. 클라이언트 ( ChatClient.java )

더보기
ChatClient.java
0.00MB
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;

public class ChatClient extends JFrame implements Runnable, ActionListener {
	private JTextField input;
	private JButton send;
	private JTextArea output;

	private Socket socket;
	private BufferedReader br;
	private PrintWriter pw;

	public ChatClient() {
		output = new JTextArea();
		JScrollPane scroll = new JScrollPane(output);
		scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		output.setEditable(false);

		input = new JTextField();

		send = new JButton("보내기");

		JPanel p = new JPanel();
		p.setLayout(new BorderLayout());
		p.add("Center", input);
		p.add("East", send);

		Container con = this.getContentPane();
		con.add("Center", scroll);
		con.add("South", p);

		setBounds(700, 200, 300, 300);
		setVisible(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				pw.println("quit");
				pw.flush();
			}
		});

	}// ChatClient()

	public void service() throws IOException {
		// 서버IP
		// String serverIP = JOptionPane.showInputDialog(this,
		// "서버IP를 입력하세요",
		// "서버IP",
		// JOptionPane.INFORMATION_MESSAGE);
		String serverIP = JOptionPane.showInputDialog(this, "서버IP를 입력하세요", "192.168.43.193");
		if (serverIP == null || serverIP.length() == 0) {
			System.out.println("서버 IP가 입력되지 않았습니다.");
			System.exit(0);
		}

		// 닉네임
		String nickName = JOptionPane.showInputDialog(this, "닉네임을 입력하세요", "닉네임", JOptionPane.INFORMATION_MESSAGE);
		if (nickName == null || nickName.length() == 0) {
			nickName = "guest";
		}

		try {
			socket = new Socket(serverIP, 9500);
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));

		} catch (UnknownHostException e) {
			e.printStackTrace();
			System.out.println("서버를 찾을 수 없습니다.");
			System.exit(0);

		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("서버와 연결이 되지 않았습니다.");
			System.exit(0);
		}
		// 서버로 닉네임 보내기
		pw.println(nickName);
		pw.flush();

		// 스레드 생성
		Thread t = new Thread(ChatClient.this); // 실행하려는 클래스 주소 입력
		t.start();

		// 이벤트
		send.addActionListener(this);
		input.addActionListener(this); // JTextField에서 엔터 단축키

	}// Service()

	public static void main(String[] args) throws IOException {
		new ChatClient().service();

	}

	@Override
	public void run() {
		// 서버로부터 받는 쪽
		String line;

		while (true) { // 내가 빠져나가지 않는 한, 데이터를 계속 받는다.

			try {

				line = br.readLine();

				if (line == null || line.toLowerCase().equals("quit")) {
					br.close();
					pw.close();
					socket.close();

					System.exit(0);
				}

				output.append(line + "\n");

				// int pos = output.getText().length();
				output.setCaretPosition(output.getText().length());

			} catch (IOException e) {
				e.getStackTrace();
			}

		}

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// 서버로 보내는 쪽

		String msg = input.getText();

		// PrintWriter 클래스는 Println으로 입력
		pw.println(msg);

		pw.flush();
		input.setText(""); // 텍스트필드 초기화

	}
}

생성자 ChatClient - JFrame 윈도우 생성 및 열기.

service - IP 및 닉네임 입력, 서버 접속, 스레드 생성, 버튼 이벤트 생성.

main - 채팅 프로그램 실행. ( ChatClient 객체 생성 및 service() 호출. )

run(스레드) - 스레드가 계속 동작하며 서버로부터 메세지 수신.

actionPerformed - 내가 보낸 메세지 내 채팅창에 띄우고 입력공간 비우기

반응형