• Home
  • About
    • ming photo

      ming

      studying

    • Learn More
    • Twitter
    • Facebook
    • Instagram
    • Github
    • Steam
  • Archive
    • All Posts
    • All Tags
    • All categories
  • categories
    • HTML+CSS+JavaScript
    • JAVA
    • Algorithm
    • DB
    • JSP
    • 정보처리기사
    • Spring
    • Thymeleaf
    • 기술면접
  • Projects

JAVA-이것이 자바다 : TCP 네트워킹(2)

09 Mar 2021

🔶 이것이 자바다 : TCP 네트워킹(2)

✔ TCP 네트워킹(2)

💡 채팅 프로그램

  • 서버 코드
      package sec07.exam03_chatting;
    
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.io.UnsupportedEncodingException;
      import java.net.InetSocketAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.Iterator;
      import java.util.List;
      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.stage.Stage;
    
      // 클래스 선언
      // Application 을 상속한다 => java fx실행클래스를 만들기 위해
      public class ServerExample extends Application {
            
          // 필드 선언
          ExecutorService executorService; // 스레드풀 생성위해 선언
          ServerSocket serverSocket; // 클라이언트의 연결요청 수락하기 위해 선언
            
          // List<Client> => 아래 class Client를  객체 생성 해 List에 저장
          // Vector : 스레드에 안전하다
          List<Client> connections = new Vector<Client>(); // 
            
          void startServer() { // 서버 시작 코드
              // start 버튼 눌렀을 때 실행
                
              // 1) ExecutorService 객체 만들기
              // executorService 라고 하는 스레드 풀 걕체 생성
              executorService = Executors.newFixedThreadPool(
                      // 스레드 최대갯수 입력
                      // 현재 PC의 CPU가 지원하는 코어의 수를 얻을 수 있다 =>코어의 수 만큼 쓰레드 생성
                      Runtime.getRuntime().availableProcessors()
                  );
                  try {
                      // 2) ServerSocket 객체 만들어 포트에 binding 한다
                      serverSocket = new ServerSocket();
                      serverSocket.bind(new InetSocketAddress("localhost",5001));
                  }catch(Exception e) {
                      if(!serverSocket.isClosed()) {
                          stopServer();
                      }
                      return; // start서버 종료
                        
                  }	
                    
                  // 3) 연결 수락 작업을 스레드에서 처리할 수 있도록 Runnable객체를 만들어
                  // executorService 에 submit 메소드를 제공하는 코드 작성
                    
                  // Runnable 객체 생성 => 연결 수락 작업 객체
                  Runnable runnable = new Runnable() {
                      // ctrl + space 해서 run메소드 오버라이드
                      @Override
                      public void run() {
                          // 연결 수락을 위한 코드 작성
                            
                          // 첫 화면에 띄울 문구 작성
                          Platform.runLater(()->{ // UI변경 코드
                              displayText("[서버시작]");
                              btnStartStop.setText("stop"); // start 버튼을 누루면 해당 버튼이 stop으로 바뀌는 코드
                          });
                            
                          // 무한루프 작성 => 클라이언트에 연결 수락 작업을 계속적으로 할 수 있는 코드 작성
                          while(true){
                              try {	
                                  // 클라이언트가 연결요청하면 accept 가 연결 수락을 하고 socket 을 리턴한다
                              Socket socket = serverSocket.accept();
                              // 로그 출력 -> 클라이언트 IP와 , 현재 담당 스레드 풀의 이름을 출력한다
                              String message = "[연결 수락 : " + socket.getRemoteSocketAddress()+ ":" 
                                                  + Thread.currentThread().getName() + "]" ;
                              Platform.runLater(()->{
                                  displayText(message);
                              });
                                
            
                              // 클라이언트 연결 하나당  클라이언트 객체를 생성해서 List<Client>에 저장한다
                              Client client = new Client(socket);
                                
                              // connections 에 클라이언트 저장
                              connections.add(client);
                                
                              // 로그 출력 => 몊개의 클라이언트가 연결 되었는지 출력
                              Platform.runLater(()->{
                                  displayText("[연결 개수 : " + connections.size()+ "]" );
                              });
                                
                              } catch (Exception e) {
                                  if(!serverSocket.isClosed()) { // 만약에 서버소켓이 닫혀있지 않다면
                                      stopServer(); // 서버를 중지한다
                                  }
                                  break;
    
                              }	
                                
                                
                          }
                      }
                  };	
                  // startServer의 맨마지막 코드
                  // 스레드 풀의 스레드 처리할 수 있게 해준다
                  executorService.submit(runnable);
          }
                
                    
          void stopServer() { // 서버 종료 코드
              // stop 버튼 눌렀을 때 실행
              try {
              // 연결된 모든 클라이언트의 연결을 끊기 위해 소켓,익스큐터 서비스 클로즈,리스크 컬렉션에서 클라이언트 제거
              Iterator<Client> iterator = connections.iterator();
              // iterator에서 가지고 올 클라이언트가 있다면
              while(iterator.hasNext()) {
                  // 클라이언트 가져오기
                  Client client = iterator.next();
                    
                  // 가지고온 클라이언트의 소켓 클로즈 => 예외처리 해야한다
                  client.socket.close();
                    
                  // 클라이언트 제거
                  iterator.remove();
                  }	
                  // Socket 클로즈
                  if(serverSocket != null && !serverSocket.isClosed()) {
                      serverSocket.close();
                  }
                  // executor 클로즈
                  if(executorService != null && !executorService.isShutdown()) {
                      executorService.shutdown();
                  }
                  // 로그 쿨력
                  Platform.runLater(()->{
                      displayText("[서버 멈춤]");
                      btnStartStop.setText("start"); // 버튼 start로 변경
                  });
                    
              } catch (Exception e) {} // 예외처리 내용 없어서 내용작성 X
                            
          }	
            
          class Client { // 데이터 통신코드
              // 필드 선언
              Socket socket;
                
              // 생성자 생성 => socket 매개값으로 받아 socket에 대입
              Client(Socket socket){
                  this.socket = socket;
                  receive(); // 클라이언트 객체를 생성할 때 항상 메소드 호출
              }
                
              // 클라이언트가 보낸 데이터 받는 메소드
              void receive() {
                  // 스레드 풀(executorService) 안에 있는 스레드가 receive 메소드를 실행 할 수 있도록
                  Runnable runnable = new Runnable() {
    
                      @Override
                      public void run() {
                          try {
                          // 클라이언트가 보낸 데이터를 받을 코드
                          while(true) {
                              byte[] byteArr = new byte[100];
                              InputStream inputStream = socket.getInputStream();
                                
                              // inputStream 을 통해 클라이언트가 보낸 데이터 받기
                              int readByteCount = inputStream.read(byteArr);
                                
                              if(readByteCount == -1) { // 클라이언트가 정상적으로 종료를 했다면
                                  throw new IOException(); // 강제적으로 IO예외를 발생
                              }
                                
                              // 로그 출력내용
                              String message = "[요청 처리 : " + socket.getRemoteSocketAddress() + ": " 
                                          + Thread.currentThread().getName() + "]";
                              // 로그 출력
                              Platform.runLater(()-> displayText(message));
                                
                              // 읽은 바이트 문자열로 변환
                              String data = new String(byteArr,0,readByteCount,"UTF-8");
                                
                              // 받은 데이터를 모든 클라이언트에게 전달
                              // for : 모든 클라이언트를 리스트에서 얻어서 send메소드를 호출해서 데이터를 클라이언트로 보낸다
                              for(Client client : connections) {
                                  client.send(data);
                                    
                              }
                          }
                            
                            
                          }catch(Exception e) {
                              try {
                              // 해당 클라이언트를 리스트에서 삭제
                                  connections.remove(Client.this);
                                    
                                  // 출력 로그 생성
                                  String message = "[클라이언트 통신 안됨 : " + socket.getRemoteSocketAddress() + ": " 
                                                  + Thread.currentThread().getName() + "]"; 
                                  // 로그 출력
                                  Platform.runLater(()-> displayText(message));
                            
                                  socket.close();
                              } catch (IOException e1) {
                              }
                                
                          }
                            
                      }
                            
                  };
                  //executorService 내부의 스레드가 run() 이라고 하는 작업을 실행하게 한다
                  executorService.submit(runnable);
                    
              }
                
              // 클라이언트로 데이터를 보내는 메소드 => 매개값으로 문자열을 받아 클라이언트로 전송한다
              void send(String data) {
                  Runnable runnable = new Runnable() {
    
                      @Override
                      public void run() {
                          // 클라이언트한테 데이터 보내는 메소드 작성
                          // 바이트배열 생성 => 보내고자 하는 매개값 data 에서 바이트 얻어낸다 
                          try {
                              byte[] byteArr = data.getBytes("UTF-8");
                                
                              // 클라이언트로 보내기
                              OutputStream outputStream = socket.getOutputStream();
                              // outputStream의 write메소드를 호출해 byteArr 에 저장된 내용을 클라이언트로 보낸다
                              outputStream.write(byteArr);
                              outputStream.flush(); // write메소드 호출후 꼭 사용해야한다
                                
                          } catch (Exception e) {
                              try {
                              // write 에서 예외 처리 발생시 출력로그 작성
                              String message = "[클라이언트 통신 안됨 : " + socket.getRemoteSocketAddress() + ": " 
                                              + Thread.currentThread().getName() + "]"; 
                              // 해당 클라이언트를 리스트에서 삭제
                              connections.remove(Client.this);
                                
                                  socket.close();
                              } catch (IOException e1) {
                              }
                          }
                      }
                        
                  };
                  //executorService 내부의 스레드가 run() 이라고 하는 작업을 실행하게 한다
                  executorService.submit(runnable);
              }
    
                
          }
            
          //////////////////////////////////////////////////////
          // 자바로 UI 만드것
          TextArea txtDisplay;
          Button btnStartStop;
            
          @Override
          public void start(Stage primaryStage) throws Exception {
              BorderPane root = new BorderPane();
              root.setPrefSize(500, 300);
                
              txtDisplay = new TextArea();
              txtDisplay.setEditable(false);
              BorderPane.setMargin(txtDisplay, new Insets(0,0,2,0));
              root.setCenter(txtDisplay);
                
              btnStartStop = new Button("start");
              btnStartStop.setPrefHeight(30);
              btnStartStop.setMaxWidth(Double.MAX_VALUE);
              btnStartStop.setOnAction(e->{
                  if(btnStartStop.getText().equals("start")) {
                      startServer();
                  } else if(btnStartStop.getText().equals("stop")){
                      stopServer();
                  }
              });
              root.setBottom(btnStartStop);
                
              Scene scene = new Scene(root);
              scene.getStylesheets().add(getClass().getResource("app.css").toString());
              primaryStage.setScene(scene);
              primaryStage.setTitle("Server");
              primaryStage.setOnCloseRequest(event->stopServer());
              primaryStage.show();
          }
            
          void displayText(String text) {
              txtDisplay.appendText(text + "\n");
          }	
            
          public static void main(String[] args) {
              launch(args);
          }
      }
    
  • 클라이언트 코드

      package sec07.exam03_chatting;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.io.UnsupportedEncodingException;
      import java.net.InetSocketAddress;
      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.stage.Stage;
    
      public class ClientExample extends Application {
          // 통신과 관련된 필드
          Socket socket;
            
          void startClient() { // 연결 시작 코드
                
              // 서버에 연결되기 전까지 대기상태로 있기위해 별도의 Thread생성 => 익명객체로 생성
              Thread thread = new Thread() {
                  @Override
                  public void run() {
                      try {
                          // socket 객체 생성
                          socket = new Socket();
                            
                          // 연결				
                          socket.connect(new InetSocketAddress("localhost", 5001));
                            
                          // 로그 출력
                          Platform.runLater(()->{
                              displayText("[연결 완료: " + socket.getRemoteSocketAddress() + "]");
                              btnConn.setText("stop"); // start버튼 stop로 변경
                              btnSend.setDisable(false); // send버튼 활성화
                          });
                      } catch (IOException e) { 
                          Platform.runLater(()->displayText("[서버 통신 안됨]"));
                          if(!socket.isClosed()) {
                              stopClient();
                          }return;
                            
                          }
                      // 연결 성공시 => receive 메소드를 받아 항상 서버가 보내는 데이터를 받는다
                          receive();
                      }
                  };		
                  thread.start(); // thread 시작
          }
            
          void stopClient() { // 연결 끊기 코드
              try {
              Platform.runLater(()->{displayText("[연결 끊음]");
              btnConn.setText("start"); // stop버튼 start로 변경
              btnSend.setDisable(true); // send버튼 비활성화	
              });
              if(socket !=null && !socket.isClosed()) {
                  socket.close();
              }
              }catch(Exception e) {}
          }
            
          void receive() { // 데이터 받기 코드
              while(true) {
                  try {
                  byte[] byteArr = new byte[100];
                  InputStream inputStream = socket.getInputStream();
                    
                  int readByteCount = inputStream.read(byteArr);
                    
                  if(readByteCount == -1) { // 클라이언트가 정상적으로 종료를 했다면
                      throw new IOException(); // 강제적으로 IO예외를 발생
                  }
                    
                  // 읽은 바이트 문자열로 변환
                  String data = new String(byteArr,0,readByteCount,"UTF-8");
                    
                  // 로그 출력
                  Platform.runLater(()-> displayText("[받기 완료]" + data));
                    
                  } catch(Exception e) {
                      Platform.runLater(()-> displayText("[서버 통신 안됨]"));
                        
                      stopClient();
                      break; // while문 빠져나가기
                  }
    
              }
                
          }
            
          void send(String data) { // 데이터 전송 코드
                
              // 별도의 스레드 만들어 데이터 보내기
                
              Thread thread = new Thread() {
                  @Override
                  public void run() {
                      try {
                          byte[] byteArr = data.getBytes("UTF-8");
                          OutputStream outputStream = socket.getOutputStream();
                          outputStream.write(byteArr);
                          outputStream.flush(); // write메소드 호출후 꼭 사용해야한다
                          Platform.runLater(()-> displayText("[보내기 완료]"));
                      } catch (Exception e) {
                          Platform.runLater(()-> displayText("[서버 통신 안됨]"));
                          stopClient();
                      }
                  }
              };
              thread.start(); // thread 시작
          }
            
          //////////////////////////////////////////////////////
            
          // 자바 UI
            
          TextArea txtDisplay;
          TextField txtInput;
          Button btnConn, btnSend;
            
          @Override
          public void start(Stage primaryStage) throws Exception {
              BorderPane root = new BorderPane();
              root.setPrefSize(500, 300);
                
              txtDisplay = new TextArea();
              txtDisplay.setEditable(false);
              BorderPane.setMargin(txtDisplay, new Insets(0,0,2,0));
              root.setCenter(txtDisplay);
                
              BorderPane bottom = new BorderPane();
                  txtInput = new TextField();
                  txtInput.setPrefSize(60, 30);
                  BorderPane.setMargin(txtInput, new Insets(0,1,1,1));
                    
                  btnConn = new Button("start");
                  btnConn.setPrefSize(60, 30);
                  btnConn.setOnAction(e->{
                      if(btnConn.getText().equals("start")) {
                          startClient();
                      } else if(btnConn.getText().equals("stop")){
                          stopClient();
                      }
                  });
                    
                  btnSend = new Button("send"); 
                  btnSend.setPrefSize(60, 30);
                  btnSend.setDisable(true);
                  btnSend.setOnAction(e->send(txtInput.getText()));
                    
                  bottom.setCenter(txtInput);
                  bottom.setLeft(btnConn);
                  bottom.setRight(btnSend);
              root.setBottom(bottom);
                
              Scene scene = new Scene(root);
              scene.getStylesheets().add(getClass().getResource("app.css").toString());
              primaryStage.setScene(scene);
              primaryStage.setTitle("Client");
              primaryStage.setOnCloseRequest(event->stopClient());
              primaryStage.show();
          }
            
          void displayText(String text) {
              txtDisplay.appendText(text + "\n");
          }	
            
          public static void main(String[] args) {
              launch(args);
          }
      }
    

    ▶ 서버 프로그램 실행 출력 server_1

▶ 서버실행 출력 server_2

▶ 클라이언트 실행 출력 client_1

▶ 클라이언트 연결 출력 연결

▶ 클라이언트2 연결 출력 연결2

▶ 클라이언트 대화 출력 send1 send2

▶ 클라이언트 중지 출력 stop

▶ 서버 중지 출력 serverStop



Share Tweet +1