본문 바로가기
Java 프로그래밍 예제

Gui 멀티 채팅 프로그램 예제

by 자유코딩 2017. 11. 3.

안녕하세요 이번 글에서는 GUI 멀티 채팅 프로그램 예제에 대해서 알아보도록 하겠습니다.

 

2개의 java파일로 구성되어 있습니다.

 

ClientFrame

import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ClientFrame extends JFrame{
    //자유롭게 사용하려면 여기에 필드로 선언해야 한다
    //채팅창 프레임을 구성하는 컴포넌트
    //textarea 한줄 이상의 문자 입력 보여주기
    
    private JTextArea textarea;
    private JTextField sendMsgTf;
    private JScrollPane scrollPane;
    //서버와의 통신을 위한 소켓
    private Socket socket;
    private BufferedWriter bw;
    
    public ClientFrame() {
        textarea = new JTextArea();
        sendMsgTf = new JTextField();
        textarea.setEditable(false);//쓰기를 금지함 edit 할 수 없는 상태
        
        scrollPane = new JScrollPane(textarea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        //As needed 즉 필요에의해서 내용이 많아지면 스크롤 바가 생긴다
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        //가로 스크롤은 안만든다
        
        setSize(500, 700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        setTitle("chatting");
    
        sendMsgTf.addKeyListener(new MsgSendListener());
        //텍스트 필드에 키 리스너를 등록
        //텍스트 필드를 지켜보고 있다가 특정 상황이 오면
        //이벤트 리스너에 정의된 내용 실행
        add(scrollPane,BorderLayout.CENTER);//프레임에 붙이기
        //add(textarea,BorderLayout.CENTER);//프레임에 붙이기
        add(sendMsgTf,BorderLayout.SOUTH);//프레임에 붙이기    
    }
    //소켓 설정을 위한 세터
    //이제 프레임도 소켓의 정보를 가지게 되었다
    public void setSocket(Socket socket) {
        this.socket = socket;
        try {
            OutputStream out = socket.getOutputStream();
            bw = new BufferedWriter(new OutputStreamWriter(out));
        } catch (Exception e) {
            e.printStackTrace();
        }
    } 


    //내부 클래스로 이벤트 리스너 만들기
    
    class MsgSendListener implements KeyListener {

        @Override
        public void keyTyped(KeyEvent e) {
            
        }
        @Override
        public void keyPressed(KeyEvent e) {
            
        }

        @Override
        public void keyReleased(KeyEvent e) {//키가 눌렸다가 떼어졌을때
            //엔터키가 눌렸다가 떼어지면 텍스트 필드에 있는 내용이 텍스트 에어리어에 나타나게
            if (e.getKeyCode()==KeyEvent.VK_ENTER) {//각각의 키들이 가지고 있는 코드 값이 나타난다
                //VK_ENTER = 상수 , 엔터 키에 대한 키값을 의미한다
                String msg = sendMsgTf.getText();
                System.out.println(msg);
                textarea.append("[ 나 ]: "+msg+"\n");
                sendMsgTf.setText("");
                try {
                    bw.write(msg+"\n");
                    bw.flush();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }//한문장이 끝났다는 것을 알리기 위해서 bufferedWriter에 "\n"을 붙인다
                
            }
            
        }
    }
    //내부 클래스로 수신 스레드 작성
    class TcpClientReceiveThread implements Runnable {
        private Socket socket;
        public TcpClientReceiveThread(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            //서버로부터 오는 메세지를 읽어서
            //텍스트 에어리어에 추가하기
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (true) {
                    String msg = br.readLine();//메세지 한줄 읽어오기
                    textarea.append("[상대방]" + msg + "\n");                    
                }
            } catch (Exception e) {
                textarea.append("연결이 종료되었습니다.");
                //System.out.println("연결이 종료되었습니다.");
            }
            finally {                
                try {
                    if (socket!=null&&!socket.isClosed()) {
                    socket.close();//다 쓴 소켓 닫기            
                    }
                } catch (Exception e2) {
                    
                }
            }
        }
    }

    public static void main(String[] args) {
        
        try {
            //서버 아이피 , 포트번호 -> 소켓 생성 -> 연결 요청
            Socket socket = new Socket("10.10.10.134", 5000);
            //소켓 객체 생성
            ClientFrame cf = new ClientFrame();
            cf.setSocket(socket);//메인에서 프레임 생성
            TcpClientReceiveThread th1 = cf.new TcpClientReceiveThread(socket);
            //TcpClientReceiveThread가 내부 클래스로 선언되어 있기 때문에
            //cf로 접근해서 socket을 전달한다
            new Thread(th1).start();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}

 

 

TcpChatServer

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

class TcpChatServerManager {
    private List<Socket> socketList;

    // 생성자 : TCSM 객체를 생성하면 소켓리스트 하나 만듬
    public TcpChatServerManager() {
        socketList = new ArrayList<Socket>();
    }

    // 소켓 추가 메소드
    public void addSocket(Socket socket) {
        this.socketList.add(socket);
        new Thread(new ReceiverThread(socket)).start();
    }

    // 멀티클라이언트와 연결을 동시에 유지하기 위한 스레드 구성
    // 각각의 소켓정보를 가지고 있어야함.
    class ReceiverThread implements Runnable {
        private Socket socket; // 소켓

        public ReceiverThread(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub

            try {
                // 클라이언트가 보낸 메시지 읽기 위한 작업
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (true) {

                    String msg = br.readLine(); // 클라이언트가 보낸 메시지 읽기
                    System.out.println(msg);

                    // 받은 메시지를 메시지 보낸 클라이언트 제외하고
                    // 모든 클라이언트한테 보내기

                    Socket tmpSocket = null;
                    try {                        
                        for (int i = 0; i < socketList.size(); i++) { // 소켓리스트를 순회하면서
                            tmpSocket = socketList.get(i);
                            // socketList.get(0) -> 소켓 객체
                            
                            if (socket.equals(tmpSocket)) continue;
                            // 메시지를 보낸 클라이언트라면 반복을 한번 건너뛰기
                            
                            // 서버가 받은 메시지를 클라이언트에 송신하기
                            BufferedWriter bw = new BufferedWriter(
                                    new OutputStreamWriter(tmpSocket.getOutputStream()));
                            
                            bw.write(msg + "\n");
                            bw.flush();
                        }
                    }catch(Exception e) {
                        System.out.println(tmpSocket.getRemoteSocketAddress() + "연결 종료");
                        socketList.remove(tmpSocket); 
                        //연결을 끊은 클라이언트를 위한 소켓 제거
                        
                        System.out.println("===============현재참여자=================");
                        for(Socket s : socketList) {
                            System.out.println(s.getRemoteSocketAddress());
                        }
                        System.out.println("==========================================");
                        
                    }
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
//                e.printStackTrace();
            }
            finally {
                if(socket != null) {
                    System.out.println(socket.getRemoteSocketAddress() + "연결 종료");
                    socketList.remove(socket); 
                    //연결을 끊은 클라이언트를 위한 소켓 제거
                    
                    System.out.println("===============현재참여자=================");
                    for(Socket s : socketList) {
                        System.out.println(s.getRemoteSocketAddress());
                    }
                    System.out.println("==========================================");
                }
            }

        }

    }
}

public class TcpChatServer {
    public static void main(String[] args) {

        // 서버소켓

        TcpChatServerManager tcsm = new TcpChatServerManager();

        try {
            ServerSocket serverSocket = new ServerSocket(5000);
            while (true) {
                Socket socket = serverSocket.accept(); // 클라이언트 연결요청 대기(연결요청 오기전엔 멈춤)
                // -> 연결요청오면? 소켓을 반환
                // 멀티 클라이언트 -> 소켓이 여러개 -> 리스트로 관리
                // -> 서버 매니져 클래스로 관리
                
                System.out.println(socket.getRemoteSocketAddress() + " : 연결");
                tcsm.addSocket(socket); // 얻은 소켓 서버매니져의 소켓목록에 추가
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
 

 

실행 화면

 

 

댓글