본문 바로가기
JAVA/JAVA Tip

[JavaFX] 채팅프로그램 개발

by MonoSoft 2022. 10. 17.
728x90
반응형

[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);

}

}

728x90
반응형

'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

댓글