활용) GUI 채팅프로그램

2019. 1. 30. 23:03JAVA

#Step1 폴더 구성





#Step2 서버 자바 프로젝트

 -> Main.java와  client.java로 나뉜다.


Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.chat;
 
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
 
 
public class Main extends Application {
    /*
     * 여러 개의 Thread를 효율적으로 관리하는 대표적인 라이브러리 -> ThreadPool로 처리하게 되면 Thread숫자를 제한하기 떄문에
     * 서버폭증에 대비할 수 있다.
     */
    public static ExecutorService threadPool;
 
    /* 접속한 클라이언트들을 관리한다 -> 일종의 배열 */
    public static Vector<client> clients = new Vector<client>();
    ServerSocket serverSocket;
 
    /* 서버를 구동시켜서 클라이언트의 연결을 기다리는 메소드 */
    public void startServer(String IP, int port) {
        try {
            serverSocket = new ServerSocket();
            /* 특정 클라이언트의 접속을 기다린다 */
            serverSocket.bind(new InetSocketAddress(IP, port));
        } catch (Exception e) {// 오류 발생
            if (!serverSocket.isClosed()) {// serverSocket이 닫혀 있는 상황이 아니라면
                stopServer();
            }
            return;
        }
        // 클라이언트가 접속할때 까지 기다린다.
        Runnable thread = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Socket socket = serverSocket.accept();// 클라이언트의 접속을 기다린다.
                        clients.add(new client(socket));
                        System.out.println("[클라이언트 접속]" + socket.getRemoteSocketAddress() + ":"
                                + Thread.currentThread().getName());
                    } catch (Exception e) {
                        if (!serverSocket.isClosed()) {
                            stopServer();
                        }
                        break;
                    }
                }
 
            }
        };
        /* threadpool을 초기화 한 다음, 첫 번째 thread를 넣어준다. */
        threadPool = Executors.newCachedThreadPool();
        threadPool.submit(thread);
    }// startServer()
    
    /* 서버의 동작을 중지시키는 메소드 */
    public void stopServer() {
        try {
            // 현재 작동 중인 모든 소켓 닫기
            /* Iterator통해서 모든 클라이언트에 개별적으로 접근할 수 있도록 할 수 있다 */
            Iterator<client> iterator = clients.iterator();
            while (iterator.hasNext()) {
                client client = iterator.next();
                client.socket.close();
                iterator.remove();
            }
            // serversocket 객체 닫기
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close();
            }
            // threadPool 종료하기
            if (threadPool != null && threadPool.isShutdown()) {
                threadPool.isShutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }// stopServer()
    /* 화면설정 */
    @Override
    public void start(Stage primaryStage){
        BorderPane root = new BorderPane();
        root.setPadding(new Insets(5));
        
        TextArea textArea = new TextArea();
        textArea.setEditable(false);
        textArea.setFont(new Font("나눔고딕",15));
        root.setCenter(textArea);
        
        Button toggleButton = new Button("시작하기");
        toggleButton.setMaxWidth(Double.MAX_VALUE);
        BorderPane.setMargin(toggleButton, new Insets(1,0,0,0));
        root.setBottom(toggleButton);
        
        String IP ="127.0.0.1";
        int port = 2980;
        
        toggleButton.setOnAction(event->{
            if(toggleButton.getText().equals("시작하기")) {
                startServer(IP, port);
                Platform.runLater(()->{
                    String message = String.format("[서버 시작]\n",IP,port);
                    textArea.appendText(message);
                    toggleButton.setText("종료하기");
                });
            }else {
                stopServer();
                Platform.runLater(()->{
                    String message = String.format("[서버 종료]\n",IP,port);
                    textArea.appendText(message);
                    toggleButton.setText("시작하기");
                });
                
            }
        });
        
        Scene scene = new Scene(root,400,400);
        primaryStage.setTitle("Chat Server");
        primaryStage.setOnCloseRequest(event->stopServer());
        primaryStage.setScene(scene);
        primaryStage.show();
        
    }// start()
 
    public static void main(String[] args) {
        launch(args);
    }// main()
}
 
cs




② client.java 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.chat;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
public class client {
    Socket socket; // 컴퓨터와 네트워크 통신을 위함
 
    public client(Socket socket) {// 생성자
        this.socket = socket;
        receive();
    }// client()
 
    /* 클라이언트로부터 메세지를 전달받는 메소드 */
    public void receive() {
        Runnable thread = new Runnable() {// 스레드 생성
 
            @Override
            public void run() {
                try {
                    while (true) {
                        /* 메세지 받기 위해 intPutStream */
                        InputStream in = socket.getInputStream();
                        byte[] buffer = new byte[512]; // 한번에 512byte만큼 전달 받을 수 있게 설정
                        int length = in.read(buffer);
                        while (length == -1)
                            throw new IOException();// 오류 발생 처리
                        /* getRemoteSocketAddress(): 현재 접속한 클라이언트의 아이피 주소와 같은 주소를 GET */
                        System.out.println("[메세지 수신 성공]" + socket.getRemoteSocketAddress() + ":"
                                + Thread.currentThread().getName());
                        /* 전달 받은 값을 한글도 가능하도록 설정 */
                        String message = new String(buffer, 0length"UTF-8");
                        /* 전달 받은 메세지를 다른 클라이언트로 보낼 수 있도록 설정 */
                        for (client client : Main.clients) {
                            client.send(message);
                        }
                    }
                } catch (Exception e) {
                    try {
                        System.out.println("[메세지 수신 오류]" + socket.getRemoteSocketAddress() + ":"
                                + Thread.currentThread().getName());
                        Main.clients.remove(client.this);
                        socket.close();    
                    } catch (Exception e2) {// 오류가 발생 했을때 더욱 더 상세히 처리
                        e2.printStackTrace();
                    }
                }
            }
        };
        Main.threadPool.submit(thread);// 만들어진 thread를 threadPool에 보낸다
    }// receive()
 
    /* 클라이어트로 메세지를 보내는 메소드 */
    public void send(String Message) {
        Runnable thread = new Runnable() {
 
            @Override
            public void run() {
                try {
                    /* 메세지를 보내기 위해 OutPutStream */
                    OutputStream out = socket.getOutputStream();
                    byte[] buffer = Message.getBytes("UTF-8");
                    /* 메세지 송신에 성공하였다면 buffer 담긴 내용을 서버로 보내고, flush를 통해 성공된 부분까지 알려줌 */
                    out.write(buffer);
                    out.flush();
                } catch (Exception e) {
                    try {
                        System.out.println("[메세지 송신 오류]" + socket.getRemoteSocketAddress() + ":"
                                + Thread.currentThread().getName());
                        /* 오류가 발생했을때 해당 클라이언트를 제거 */
                        Main.clients.remove(client.this);
                        socket.close();// 닫아버림
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    }
                }
 
            }
        };
        Main.threadPool.submit(thread);
    }// send()
}// client{}
 
cs


③ 실행화면








#Step3  클라이언트 폴더 

-> 채팅서버에 접속하는 클라이언트를 설정


①Main,java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package application;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
 
public class Main extends Application {
    Socket socket;
    TextArea textArea;
 
    // 클라이언트 동작 메소드
    public void startClient(String IP, int port) {
        Thread thread = new Thread() {
            public void run() {
                try {
                    socket = new Socket(IP, port);
                    receive();
                } catch (Exception e) {
                    // TODO: handle exception
                    if (!socket.isClosed()) {    
                        stopClient();
                        System.out.println("[서버 접속 실패]");
                        Platform.exit();// 프로그램 종료
                    }
                }
            }
        };
        thread.start();
    }
 
    // 클라이언트 종료 메소드
    public void stopClient() {
        try {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    // 서버로부터 메세지를 전달받는 메소드
    public void receive() {
        while (true) {
            try {
                InputStream in = socket.getInputStream();
                byte[] buffer = new byte[512];
                int length = in.read(buffer);
                if (length == -1)
                    throw new IOException();
                String message = new String(buffer, 0length"UTF-8");
                Platform.runLater(() -> {
                    textArea.appendText(message);
                });
            } catch (Exception e) {
                // TODO: handle exception
                stopClient();
                break;
            }
        }
    }
 
    // 서버로 메세지를 보내는 메소드
    public void send(String message) {
        Thread thread = new Thread() {
            public void run() {
                try {
                    OutputStream out = socket.getOutputStream();
                    byte[] buffer = message.getBytes("UTF-8");
                    out.write(buffer);
                    out.flush();
                } catch (Exception e) {
                    // TODO: handle exception
                    stopClient();
                }
            }
        };
        thread.start();
    }
 
    // 동작 메소드
    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();
        root.setPadding(new Insets(5));
 
        HBox hBox = new HBox();
        hBox.setSpacing(5);
 
        TextField userName = new TextField();
        userName.setPrefWidth(150);
        userName.setPromptText("닉네임을 입력하세요");
        HBox.setHgrow(userName, Priority.ALWAYS);
 
        TextField IPText = new TextField("127.0.0.1");
        TextField portText = new TextField("2980");
        portText.setPrefWidth(80);
        IPText.setPrefWidth(150);// 픽셀단위
 
        hBox.getChildren().addAll(userName, IPText, portText);
        root.setTop(hBox);
 
        textArea = new TextArea();
        textArea.setDisable(false);
        root.setCenter(textArea);
 
        TextField input = new TextField();
        input.setPrefWidth(Double.MAX_VALUE);
        input.setDisable(true);
 
        /* 텍스트 입력창 동작 */
        input.setOnAction(event -> {
            send(userName.getText() + ":" + input.getText() + "\n");
            input.setText("");
            input.requestFocus();
        });
 
        /* 보내기 버튼 동작 */
        Button sendBtn = new Button("전송");
        sendBtn.setDisable(true);
 
        sendBtn.setOnAction(event -> {
            send(userName.getText() + ":" + input.getText() + "\n");
            input.setText("");
            input.requestFocus();
        });
 
        /* 접속 버튼 동작 */
        Button loginBtn = new Button("접속");
 
        loginBtn.setOnAction(event -> {
            if (loginBtn.getText().equals("접속")) {
                int port = 2980;
                try {
                    port = Integer.parseInt(portText.getText());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                startClient(IPText.getText(), port);
                Platform.runLater(() -> {
                    textArea.appendText("[채팅 접속]\n");
                });
                loginBtn.setText("종료");
                input.setDisable(false);
                sendBtn.setDisable(false);
                input.requestFocus();
            }else {
                stopClient();
                Platform.runLater(()->{
                    textArea.appendText("[채팅 종료]\n");
                });
                loginBtn.setText("접속");
                input.setDisable(true);
                sendBtn.setDisable(true);
            }
        });
 
        BorderPane pane = new BorderPane();
        pane.setLeft(loginBtn);
        pane.setCenter(input);
        pane.setRight(sendBtn);
        
        root.setBottom(pane);
        Scene scene = new Scene(root,400,400);
        primaryStage.setTitle("Chat Client");
        primaryStage.setScene(scene);
        primaryStage.setOnCloseRequest(event-> stopClient()); // 닫기 버튼을 눌렸을때 stopClient메소드 호출
        primaryStage.show();
        
        loginBtn.requestFocus();
        
        
    }
 
    // 실행 메소드
    public static void main(String[] args) {
        launch(args);
    }
}
 
cs



 실행화면




#Step4 프로그램 동작




>>서버가 시작되지 않은 상태에서 클라이언트만 실행시킬 경우 오류 발생



>>각 클라이언트가 접속할 때 Thread 생성