Java 프로그래밍 예제

Gui 멀티 채팅 프로그램 예제

iIxmont 2017. 11. 3. 11:41

안녕하세요 이번 글에서는 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();
        }

    }
}
 

 

실행 화면