자바 멀티 채팅 프로젝트

Giho Kim·2023년 11월 7일
0

포스코

목록 보기
5/6

서버 - 여러명의 사용자가 한 서버에서 채팅을 할 수 있게 중계자 역할

javafx - 자바 기반 GUI 라이브러리

Client

public class Client {
	Socket socket; //네트워크상에서 통신하기 위해
    
    public Client(Socket socket) { //생성자 어떠한 변수의 초기화를 위해
    	this.socket = socket;
        receive(); //반복적으로 클라이언트로 부터 어떠한 메시지를 전달받을수있도록
    }
    
    //클라이언트로부터 메시지를 전달받는 메소드
    public void receive() {
    	Runnable thread = new Runnable() {
        	//Runnable은 run 함수를 반드시 가지고 있어야됨
            @Override
            public void run() { 하나의 쓰레드가 어떠한 모듈로써 동작을 할껀지 정의하는 함수
            	try {
                	while (true) { //반복적으로 클라이언트에게 무엇인가 전달받을수 있도록하기위해
                    	InputStream in = socket.getInputStream(); //어떠한 내용을 전달받을수있도록하기위해
                        byte[] buffer = new byte[512];
                        int length = in.read(buffer);
                        while (length == -1) throw new IOException();
                        System.out.println("[메시지 수신성공]"
                        + socket.getRemoteSocketAddress()
                        + ": " + Thread.currentThread().getName()); //클라이언트 주소 + 각 쓰레드별 이름 (고유정보)
                        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.threadPool.submit(thread); //threadpool에 등록
    }
    
    //클라이언트에게 메시지를 전송하는 메소드
    public void send(String message) {
    	Runnable thread = new Runnable() {
        	@Ovverride
            public void run() {
            	try {
                	OutputStream out = socket.getOutputStream(); // outputstream --> 송신을 위한 객체
                    out.wirte(buffer); //버퍼의 닮긴 내용을 서버에서 클라이언트로 전송
                    out.flush();
                } catch (Exception e) {
                	try {
                    	System.out.println("[메시지 송신 오류]"
                        + socket.getRemoteSocketAddress
                        + ": " + Thread.cureentThread().getName());
                        Main.clients.remove(Client.this); //오류가 발생했다면 메인 함수안의 오류가 난 클라이언트를 지워줌
                        socket.close(); //오류가 난 클라이언트의 소켓을 닫음
                    }
                }
            }
        };
        
        Main.threadPool.submit(thread);
    }
}

Main (서버)

public class Main extend Application {
	public static ExecutorService threadPool; //다양한 클라이언트가 접속했을때 스레드들을 효과적으로 관리하기 위해
    public static Vector<Client> clients = new Vector<Client>(); //vector --> 배열
    
    ServerSocket serverSocket;
    
    //서버를 구동시켜서 클라이언트의 연결을 기다리는 메소드
    public void startServer() {
    	try {
        	serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(IP, port);
            
        } catch (Exception e) {
        	e.printStackTrace();
            if (!serverSocket.isClosed()) {
            	stopServer();
            }
            return;
        }
        
        //클라이언트가 접속할때까지 계속 기다리는 쓰레드
        Runnable thread = new Runnable() {
        	while (true) {
            	Socket socket = serverSocket.accpet(); //accept가 호출되면 실행 멈추고 client 계속 기다림 
                clients.add(new Client(socket)); //클라이언트 접속
                System.out.println("클라이언트 접속"
                + socket.getRemoteSocketAddress()
                + ": " + Thread.currentThread().getName()); //로그
                
            } catch (Exception e) {
            	if (!serverSocket.isClosed()) {
                	stopServer(); //서버 소켓에서 문제가 일어남
                }
                break;
                
            }
            
        };
        threadPool = Executors.newCachedThreadPool(); //쓰레드풀 초기화
        threadPool.submit(thread); //쓰레드풀에 현재 클라이언트를 기다리는 쓰레드 담을수있도록 처리
    }
   
    
    public void stopServer () {
    	try {
        	//현재 작동중인 모든 소켓 담기
            
            Iterator<Client> iterator = clients.iterator();
            while(Iterator.hasNext()) {
            	Client client = iterator.next();
                client.socket.close();
                iterator.remove();
            }
            
            //서버소켓이 null이 아니고 열려있다면 --> 닫으셈
            if (serverSocket != null && !serverSocket.isClosed()) {
				serverSocket.close();            	
            }
            
            // threadPool도 열려있다면 닫아라
            if (threadPool != null && !threadpool.isShutdown()) {
            	threadPool.shutdown();
            }
        } catch (Exception e) {
        	e.printStackTrace();
        }
    }
    
    //ui를 생성하고 실질적으로 프로그램을 동작시키는 메소드
    @Override
    public void start(Stage primaryState) {
    	// 레이아웃
        BoarderPane 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);
        BorderPand.setMargin(toggleButton, new Insets(1, 0, 0, 0));
        root.setBottom(toggleButton);
        
        String IP = "127.0.0.1";
        int port = 9876;
        
        toggleButton.setOnAction(event -> {
        	if (toggleButton.getText().equals("시작하기")) {
            	startServer(IP, port);
                Platform.runLater(() -> {
                	String message = String.format("서버시작", IP, port);
                    textArea.appendText(message);
                    toggleButton.setText("종료하기");
                    
                });
            } else {
            	stopServer();
                Platform.runLater(() -> {
                	String message = String.format("서버종료", IP, port);
                    textArea.appendText(message);
                    toggleButton.setText("시작하기");
                });
            }
        });
        Scence 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);
    }
}

Client Project --> 아예 다른 컴퓨터에서 접속하는 것이므로 다른 프로젝트로 생성

  • main
public class Main extends Application {
	Socket socket;
    TextArea textArea;
    
    //클라이언트 프로그램 동작 메소드
    public void startClient(String IP, int port) { //동시다발적으로 스레드 생성 안해도 됨 -->  runnable대신 thread
    	Thread thread = new Thread() {
        	public void run() {
            	try {
                	socket = new Socket(IP, port);
                } 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) {
            
            }
        }
    }
    
    //서버로 메시지를 전송하는 메소드
    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) {
    	BoarderPane root = new BoarderPane();
        root.setPadding(new Insets(5));
        
        HBOX hbox = new HBOX();
        hbox.setSpacing(5);
        
        TextField userName = new TextField();
        userName.setPrefWidth(150);
        userName.setPromptText("닉네임을 입력하세요");
        
    }
    
    // 프로그램 진입점
    public static void main(String[] args) {
    	launch(args);
    }
}
profile
취준돌이 개발자 김기호

0개의 댓글