[JavaFX] 채팅프로그램 개발
1.서버 프로그램
************************************* 메인 클래스
package application;
import java.awt.Font;
import java.awt.Label;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.Clock;
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.TextArea;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
//쓰레드풀을 이용해서 다양한 클라이언트 접속했을때 효과적으로 관리
public static ExecutorService threaapool;
//접속한 클라이언트를 관리할수있는것
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) {
e.printStackTrace();
if(!serverSocket.isClosed()) {
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;
}
}
}
};
//성공시 쓰레드풀 초기화
threaapool = Executors.newCachedThreadPool();
//현재 쓰레드풀에 접속한 클라이언트 쓰레드를 넣어줌
threaapool.submit(thread);
}
// 서버의 작동을 중지시키는 메소드입니다.
public void stopServer() {
try {
//현재 작동 중인 모든 소켓 닫기
//Iterator 를 이용해서 모든 클라이언트 접슥할수있게 접근
Iterator<Client> iterator = clients.iterator();
//모두 루프 돌리기
while(iterator.hasNext()) {
Client client = iterator.next(); //다음 클라이언트
client.socket.close(); //끊기
iterator.remove(); //해당 iterator도 제거
}
//서버 소캣 객체 닫기
if(serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
//쓰레드풀도 종료하기
if(threaapool != null && !threaapool.isShutdown()) {
threaapool.isShutdown();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// UI를 생성하고 , 실질적으로 프로그램을 동작시키는 메소드입니다.
@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 javafx.scene.text.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 = 9876; //포트 설정
//이벤트 클릭시 처리
toggleButton.setOnAction(event -> {
//toggleButton 버튼 문자가 시작하기면
if(toggleButton.getText().equals("시작하기"))
{
//서버 시작
startServer(IP, port);
//runLater를 이용해서 GUI 요소를 나타내도록 한다. 그냥 출력하라
Platform.runLater( () -> {
//message에 메세지 문구 생성
String message = String.format("[서버시작] \n",IP,port);
//메세지 추가
textArea.appendText(message);
//종료하기로 텍스트 변경
toggleButton.setText("종료하기");
});
} else
{
stopServer();
//runLater를 이용해서 GUI 요소를 나타내도록 한다. 그냥 출력하라
Platform.runLater( () -> {
//message에 메세지 문구 생성
String message = String.format("[서버종료] \n",IP,port);
//메세지 추가
textArea.appendText(message);
//종료하기로 텍스트 변경
toggleButton.setText("시작하기");
});
}
});
//화면크기 설정
Scene scene = new Scene(root, 400,400);
//화면 타이틀 설정
primaryStage.setTitle("[ 채팅 서버 ] ");
//종료버튼 클릭시 스텁이벤트 설정
primaryStage.setOnCloseRequest(event -> stopServer());
//화면 몰라
primaryStage.setScene(scene);
//보여주기
primaryStage.show();
}
//프로그램의 진입점입니다.
public static void main(String[] args) {
launch(args);
}
}
*************************************클라이언트
package application;
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();
}
//클라이언트로부터 메세지를 전달 받는 메소드입니다.
public void receive() {
//일반적으로 한가지의 쓰레드를 사용할때 Runnable 객체를 많이 사용한다.
Runnable thread = new Runnable()
{
//Runnable 라이브러리는 run함수를 꼭 가지고 있는다.
@Override
public void run()
{
try
{
while(true)
{
//스트림으로 받아옴
InputStream in = socket.getInputStream();
//한번에 512만큼 받는다.
byte[] buffer = new byte[512];
//담음 버퍼의 크기를 측정
int length = in.read(buffer);
//메세지가 없거나 오류면 출력
while(length == -1) throw new IOException();
//정상이면 메세지 수신
System.out.println("[메시지 수신성공] "
+ socket.getRemoteSocketAddress() //주소정보
+ ": " + Thread.currentThread().getName()); //쓰레드릐 정보 즉데이터
//전달받은값을 한글도 인코딩 처리해서 message 변수에 담는다.
String message = new String(buffer, 0 , length, "UTF-8");
//전달받은 메세지를 다른 클라이언트에 보낼수있게 한다.
for (Client client : Main.clients)
{
client.send(message);
}
}
} catch(Exception e) {
try {
System.out.println("[메시지 수진 오류] "
+ socket.getRemoteSocketAddress() //주소 정보
+ ": " + Thread.currentThread().getName()); //메세지 내용
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
};
//메인에 쓰레드풀에 등록시켜줌.
Main.threaapool.submit(thread);
}
//클라이언트에게 메시지를 전송하는 메소드
public void send(String message) {
Runnable thread = new Runnable()
{
@Override
public void run() {
try {
//OutputStream 담기 java.io 임포트
OutputStream out = socket.getOutputStream();
//한글 인코딩 해서 버퍼에 담기
byte[] buffer = message.getBytes("UTF-8");
//버퍼에 담은걸 서버에서 클라이언트로 전송
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.threaapool.submit(thread);
}
}
2.클라이언트 소스
************************************* 메인 클래스
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.stage.Stage;
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;
public class Main extends Application {
Socket socket;
TextArea textArea; //import javafx.scene.control.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) {
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,0,length,"UTF-8");
Platform.runLater(()->{
textArea.appendText(message);
});
} catch (Exception e) {
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) {
stopClient();
}
}
};
thread.start();
}
//실제로 프로그램 동작시키는 메소드
@Override
public void start(Stage primaryStage) {
try {
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); //h박스 내부에서 해당 텍스를 출력할수있도록 해준다.
TextField IPText = new TextField("127.0.0.1");
TextField portText = new TextField("9876");
portText.setPrefWidth(80);//넓이
hbox.getChildren().addAll(userName, IPText,portText);
root.setTop(hbox);
textArea = new TextArea();
textArea.setEditable(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 sendButton = new Button("보내기");
sendButton.setDisable(true);
sendButton.setOnAction(event -> {
//전송
send(userName.getText() + ": " + input.getText() + "\n");
//빈칸
input.setText("");
//셋포터스
input.requestFocus();
});
Button connectionButton = new Button("접속하기");
connectionButton.setOnAction(event -> {
if(connectionButton.getText().equals("접속하기")) {
int port = 9876;
try {
port = Integer.parseInt(portText.getText());
} catch (Exception e) {
e.printStackTrace();
}
startClient(IPText.getText(), port);
Platform.runLater(() -> {
textArea.appendText("[ 채팅방접속 ]\n");
});
connectionButton.setText("종료하기");
input.setDisable(false);
sendButton.setDisable(false);
input.requestFocus();
} else {
stopClient();
Platform.runLater(() -> {
textArea.appendText("[ 채팅방 퇴장 ] \n");
});
connectionButton.setText("접속하기");
input.setDisable(true);
sendButton.setDisable(true);
}
});
BorderPane pane = new BorderPane();
pane.setLeft(connectionButton);
pane.setCenter(input);
pane.setRight(sendButton);
root.setBottom(pane);
Scene scene = new Scene(root , 400,400);
primaryStage.setTitle("[ 채팅 클라이언트 ] ");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(event -> stopClient());
primaryStage.show();
connectionButton.requestFocus();
} catch(Exception e) {
e.printStackTrace();
}
}
//프로그램의 진입점입니다.
public static void main(String[] args) {
launch(args);
}
}
'JAVA > JAVA Tip' 카테고리의 다른 글
이클립스에서 자바프로젝트 열기 (0) | 2022.10.19 |
---|---|
[JavaFX] 리듬게임 만들기 (0) | 2022.10.18 |
JavaFX 설치 후 프로젝트 실행 오류 (0) | 2022.10.12 |
이클립스 설치 방법 (0) | 2022.10.11 |
JDK 설치 방법 (0) | 2022.10.07 |
댓글