JAVA

Thread를 사용하여 Socket 통신하는 방법

Thread와 Socket 통신 이해하기

Thread 개념

spring
Thread 개념 완벽하게 이해하기Thread는 타임슬라이싱(Time Slicing)하여 문맥교환(Context Switching)을 수행하면서 CUP에게 여러개의 일을 동시에 하도록 만드는 일을 한다....

Thread 특징 요약

  • Thread는 static 함수로 생명 주기가 함수 안에서 시작하고 끝난다.
  • Thread는 트립(trip)시 문맥을 알아야한다.
  • Thread는 Runnable 타입이여야하고 반드시 run 함수를 가지고 있어야한다.
  • Main Thread는 Sub Thread를 실행만 시키고 자기 일을 한다.
  • 자바는 Thread가 하나라도 돌아가고 있으면 종료되지 않는다.

Thread  예제

예제1
package ex01;

//새로운 쓰레드는 반드시 러너블 타입이여야하고 run함수를 가지고 있어야한다.
class NewThread implements Runnable{

  @Override
  public void run() { // target
    for (int i = 1; i < 11; i++) {
      System.out.println("새로운 스레드 : " + i);
      try {
        Thread.sleep(1000); // 메인 스레드가 1000ms 쉬게 만든다.
      } catch (InterruptedException e) { // Interrupted : 방해하다. 간섭하다.
        e.printStackTrace();
      } 
    }
  }
    
}

public class ThreadEx01 {

  public static void main(String[] args) { // 생명 시작
    
    // **새로 만든 서브 쓰레드 사용하기**
    Thread t1 = new Thread(new NewThread()); // 괄호 안에는 target(run 함수를 들고 있는 대상)이 들어간다. 
    t1.start(); // 새로운 쓰레드 시작하기 -> run 함수가 실행
    
    for (int i = 1; i < 11; i++) {
      System.out.println("메인 스레드 : " + i);
      try {
        Thread.sleep(1000); 
      } catch (InterruptedException e) { 
        e.printStackTrace();
      } 
    }
    
    //생명 끝 
  }
  
}
예제2
package ex01;

// 메인 쓰레드의 일이 끝나더라도 서브 쓰레드는 종료되지 않는다.
class NewThread2 implements Runnable{

  @Override
  public void run() { 
    for (int i = 1; i < 21; i++) {
      System.out.println("새로운 스레드 : " + i);
      try {
        Thread.sleep(1000); 
      } catch (InterruptedException e) { 
        e.printStackTrace();
      } 
    }
  }
    
}

public class ThreadEx02 {

  public static void main(String[] args) {
    
    Thread t1 = new Thread(new NewThread2());
    t1.start();
    
    for (int i = 1; i < 11; i++) {
      System.out.println("메인 스레드 : " + i);
      try {
        Thread.sleep(1000); 
      } catch (InterruptedException e) {
        e.printStackTrace();
      } 
    }
    
  }
  
}

 

Socket 통신

웹 서버에서 통신은 요청이 들어왔을 때 단발적으로 이루어지는데 반해 소켓 통신은 연결이 계속 이어지는 stateful 서버를 사용한다.

Thread를 사용하여 Socket 통신하는 방법

원리 이해하기

  1. 서버 소켓과 클라이언트 소켓이 같은 포트에 연결한다.
  2. 클라이언트가 접속할 때까지 락을 걸어준다.(accept 함수를 사용 : 내부적으로 while문이 돌고 있다.)
  3. OS의 상시 감시자 리스너에 의해 클라이언트의 접속을 확인한다.(accept 함수 호출)
  4. 클라이언트가 접속하면 Thread를 새로 생성하여 서브 소켓과 연결하고 서버 소켓과의 연결은 끊는다.
  5. 클라이언트와 소켓에 각각 Buffer를 달아 서로 통신할 수 있게 만든다.
  6. 서버 소켓은 또 다른 클라이언트의 통신을 받을 수 있게 된다.

예제로 이해하기

예제1.

서버 소켓과 클라이언트 소켓을 만들어 서로 통신할 수 있게 만든다.

package ex02;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

// 소켓 서버 = stateFul  
public class MyServerSocket {
  
  private ServerSocket serverSocket;
  private Socket socket;
  private BufferedReader br;
  
  public MyServerSocket() {
    try {
      serverSocket = new ServerSocket(10000);
      System.out.println("클라이언트로부터 접속 대기중");
      socket = serverSocket.accept(); // accept(리스너에 의해 호출) : 클라이언트가 접속할 때까지 락이 걸림. -> 내부적으로 while(반복) 돌고있다.
      System.out.println("클라이언트 연결 완료");
      
      // getInputStream : 클라이언트로부터 읽는다. BufferedReader :  바이트를 문자로 바꿔서 읽음
      br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
      
      String input =null;
      
      while((input = br.readLine()) != null) {
        System.out.println("클라이언트 : "+input);
      }
      
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args) {
    new MyServerSocket();
  }
  
}
package ex02;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyClientSocket {
  
  private Socket socket;
  private BufferedWriter bw;
  private PrintWriter pw;
  
  private MyClientSocket() {
    try {
      socket = new Socket("localhost",10000);
      bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
      //pw = new PrintWriter(socket.getOutputStream(), true); //auto Flush
      
      Scanner sc = new Scanner(System.in);
      while(true) {
        String keyboardInput = sc.nextLine(); // 락 걸림(내부적으로 리스너 작동중)
        //pw.println(); printWriter는 이게 \n 대신해줌
        bw.write(keyboardInput+"\n"); // \n : 소켓 통신에서 메시지의 끝을 의미 
        bw.flush();
      }
      
    } catch (UnknownHostException e) { 
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    new MyClientSocket();
  }
  
}
예제2.

서버 소켓과 클라이언트 소켓을 만들어 여러 명이 통신할 수 있게 만든다.

package ex03;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

// 소켓 서버 = stateFul  
public class MyServerSocket {
  
  private ServerSocket serverSocket;
  
  public MyServerSocket() {
    try {
      serverSocket = new ServerSocket(10000);
      while(true) { // 메인 스레드 = 데몬 스레드 
        System.out.println("클라이언트로부터 접속 대기중");
        // Socket을 지역 변수로 받아 하나의 소켓을 공유하지 않게 만든다.
        Socket socket = serverSocket.accept(); // accept(리스너에 의해 호출) : 클라이언트가 접속할 때까지 락이 걸림. -> 내부적으로 while(반복) 돌고있다.
        System.out.println("클라이언트 연결 완료");
        
        Thread t = new Thread(new InnerThread(socket)); // 지역 변수로 선언한 소켓 넘겨
        t.start();  
      }
      
    } catch (IOException e) {
      System.out.println("클라이언트와의 연결에 실패하였습니다.");
    }
  }
  
  // 내부 클래스 -> 나만 사용할 때
  class InnerThread implements Runnable {

    private BufferedReader br;
    private Socket socket;
    
    public InnerThread(Socket socket) { // 소켓을 받아준다.
      this.socket = socket;
    }

    @Override
    public void run() {
      
      try {
        // getInputStream : 클라이언트로부터 읽는다. BufferedReader :  바이트를 문자로 바꿔서 읽음
        br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
        
        String input =null;
        
        // 겁나 바빠요 -> target 만들어서 일을 맡긴다. 
        while((input = br.readLine()) != null) {
          System.out.println("클라이언트 : "+input);
        }
      } catch (Exception e) {
        System.out.println("클라이언트와의 연결이 종료되었습니다.");
        try {
          socket.close();
          br.close();
        } catch (Exception e2) {
          System.out.println("메모리 릭이 발생하였습니다.");  
        }
      }
      
    
    }
    
  }
  
  public static void main(String[] args) {
    new MyServerSocket();
  }
  
}
package ex03;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyClientSocket {
  
  private Socket socket;
  private BufferedWriter bw;
  private PrintWriter pw;
  
  private MyClientSocket() {
    try {
      socket = new Socket("localhost",10000);
      bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
      //pw = new PrintWriter(socket.getOutputStream(), true); //auto Flush
      
      Scanner sc = new Scanner(System.in);
      while(true) {
        String keyboardInput = sc.nextLine(); // 락 걸림(내부적으로 리스너 작동중)
        //pw.println(); printWriter는 이게 \n 대신해줌
        bw.write(keyboardInput+"\n"); // \n : 소켓 통신에서 메시지의 끝을 의미 
        bw.flush();
      }
      
    } catch (UnknownHostException e) { 
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    new MyClientSocket();
  }
  
}

 

socket

개념이해를 위한 참고 포스팅

spring
스프링을 배우기 위해 꼭 알아야할 자바 기본 문법스프링은 IoC(Inversion of Control) 컨테이너 관리권을 가지고 있어 어노테이션을 사용하여 의존성 주입을 한다......
spring
스프링을 배우기 위해 꼭 알아야할 웹 용어 정리스프링부트 2강 -웹 기본 개념 html, http, browser, url 아파치와 톰캣 Stream Stateless와 Stateful Session과 Cookies Tomcat의 Scope...
Commu
Thread와 비동기 비교하기동기적 프로그래밍은 테스크(Task)를 순차적, 직렬적으로 수행하도록 하여 어떤 테스크가 수행 중이면 다음 테스크는 대기하게하는 방식의 프로그래밍이다....
최신글