JAVA/자바 네트워크 프로그래밍

Java 소켓 통신(서버)

반응형

Java 소켓 통신(서버) 


서버 소켓은 서버에서 실행되며들어오는 TCP 연결을 기다린다.

자바는 본질적으로 ServerSocket 클래스를 제공한다,

 

서버 소켓 생성하기 (기본 생성방법)

4개의 생성자를 제공한다상황에 따라port, 요청  길이InetAddress 받아 생성   있고아무 정보가 없는 ServerSocket 파일도 생성이 가능하다.


-> 특정 InetAddress 객체를 전달 받았을 경우 입력받은 지정된 주소로 들어오는 연결에 대해서만 서버 소켓을 대기한다.

->포트번호를 0으로 설정할 경우 시스템은 사용할  있는 포트를 임의로 선택한다.

   사용자에게 임의로 선택된 포트는 미리 알수 없기에 익명 포트라고 한다익명 포트는 FTP 같은 다수의 소켓을 사용하는 프로토콜에 종종 사용된다.

 

해당 생성자에서 발생하는 예외는 IOException BindException이다.

해당 예외는 요청한 예외가 이미 사용 중이거나루트 권한이 없는 1 ~ 1023 사이의 포트에 바인딩을 시도한 경우다.

 

아무런 포트 바인딩이 되지 않은 경우에는 생성  bind()메소드를 통해 바인딩을 해주지 않으면 사용할  없다.

 

 

실행 흐름

  1. ServerSocket() 생성자를 사용하여 특정 포트 대한 새로운 ServerSocket 생성한다.
  2. ServerSocket() 자신의 accept()메소드를 사용하여 특정 포트로 들어오는 연결요청을 대기한다Accept() 클라이언트가연결을 시도할  까지 블로킹되며연결을 맺어진  클라이언트와 서버를 연결하는 Socket 객체를 반환한다.
  3. 서버 유형에 따라 Socket getInputStream() 메소드나  getOutputStream() 메소드를 호출하여 클라이언트와 통신에 필요한 입출력 스트림을 얻을  있다.
  4. 서버와 클라이언트는 연결을 종료하기 전까지 미리 정의된 프로토콜에 따라 대화한다.
  5. 서버나 클라이언트가 또는 둘다 연결을 종료한다.
  6. 서버는 다시 2번으로 돌아가 다음 연결을 기다린다.

 

시간을 반환하는 소켓 만들기

ServerSocket server = new ServerSocket(13); // 13 포트에서 대기하는 서버 소켓 생성

Socket connection = server.accept(); //accept() 호출되면 실행이 블록킹 된다클라이언트와 연결  때까지 무한 대기

OutputStream out = connection.getOutputStream();

Writer writer = new OutputStreamWriter(out, "ASCII");

 

Date now = new Date();

Out.write(now.toString +"\r\n"); // 캐리지 리턴과 라인피드의 쌍의 사용법에 주의하라.

Out.flush();

Connection.close();

 

위에 작업을 계속 반복적으로 수행하고 싶을 경우에

 

ServerSocket server = new ServerSocket(13); // 13 포트에서 대기하는 서버 소켓 생성

Try (Socket connection = server.accept(); //accept() 호출되면 실행이 블록킹 된다클라이언트와 연결  때까지 무한 대기) {

While(true) {

OutputStream out = connection.getOutputStream();

Writer writer = new OutputStreamWriter(out, "ASCII");

 

Date now = new Date();

Out.write(now.toString +"\r\n"); // 캐리지 리턴과 라인피드의 쌍의 사용법에 주의하라.

Out.flush();

Connection.close();

}

} catch (IOException ex) {}

 

다음과 같이 처리할  있다이런 서버를 반복서버라고 한다.

-> 하지만 이런 서버는 느린 클라이언트 하나가 서버 자체를 느리게   있는 단점이 있다.

-> 해당 서버는 수동으로 멈추기 전까지 무한 대기 상태에 빠져있다.

 

 

멀티스레드 서버

운영체제는 특정 포트를 통해 들어오는 연결 요청을 FIFO 큐에 저장한다.


자바는 기본 적으로  큐의 길이를 50으로 설정하지만운영체제마다 다르다.큐는 아직 처리가 되지 않은 연결로 가득  경우 호스트는 해당 포트의 큐에 빈자리가  때까지 추가적인 연결을 거부한다.


->ServerSocket 생성자는 큐의 기본길이를 변경할  있는 기능을 제공하지만 운영체제가 지원하는 최대 길이 이상을 증가  없기에

많은 연결이 맺어 질때 어려움이 발생한다.

 

위의 증상의 해결 방법은 큐에 추가되는 새로운 연결을 수용하는 스레드와 분리된 별도의 스레드를  연결마다  당하는 것이다.

 

위의 작성한 예제를  스레드로 분리해 보자.

Ex) 멀티 스레드 daytime 서버

Try(ServerSocket server = new ServerSocket(PORT)) {

While(true) {

Try {

Socket connection = server.accept();

Thread task = new DaytimeThread(connection);

Task.start();

} catch (IOException ex) {}

}

} catch (IOException ex) {

}

}

 

Private static class DayTimeThread extends Thread {

Private Socket connection;

 

DaytimeThread(Socket connection) {

This.connection = connection;

}

 

Public void run() {

Try {

Writer out = new OutputStreamWriter(connection.getOutputStream());

Date now = new Date();

Out.write(now.toString() +"\r\n");

Out.flush();

} catch (IOException ex) {

System.err.println(ex);

} finally {

Connection.close();

} catch (IOException e) {

}

}

}

}

 

-> 이런 방식으로 들어오는 연결에 대해 처리하는 비즈니스 로직을 쓰레드로 처리하게 되면서  연결마다 처리되는 로직이 쓰레드로 관리되어 서로의 동작이 영향을 받지 않느다.


-> 하지만 다음과 같은 방식은 무제한의 쓰레드를 생성하기에 메모리 이슈가 발생할  있다이를 해결 하기 위해 쓰레드 풀을 사용한다.

 

ExecutorService pool = Executors.newFixedThreadPool(50);

Try(ServerSocket server = new ServerSocket(PORT)) {

While(true) {

Try {

Socket connection = server.accept();

Callable<Void> task = new DaytimeTask(connection);

Pool.submit(task);

} catch (IOException ex) {}

}

} catch (IOException ex) {

}

}

 

Private static class DayTimeTask implements Callable<Void> {

 

Private Socket connection;

 

DaytimeThread(Socket connection) {

This.connection = connection;

}

 

Public void run() {

Try {

Writer out = new OutputStreamWriter(connection.getOutputStream());

Date now = new Date();

Out.write(now.toString() +"\r\n");

Out.flush();

} catch (IOException ex) {

System.err.println(ex);

} finally {

Connection.close();

} catch (IOException e) {

}

}

}

}

 

 

 

서버 소켓은 Serversocket.close() 메소드를 통해 연결을 종료한다.

종료된 소켓은 어떠한 방식으로도  연결을   없다Java 7에서 소개된 방식을 사용하여 try resource catch 문장을 사용하여자동으로 자원을 해제   있다.

 


로그 남기기

일반적으로  가지의 내용을 로그에 남긴다.

  1. 요청
  2. 서버 에러

 

감사로그(요청) 연결마다 남기거나연결에서 다수의 동작이 있을 경우 연결의 동작마다 로그를 남긴다.

에러 로그는 서버 운영 중에 발생하는 예측되지 않은 예외를 기록한다.

클라이언트와의 연결 종료와 같은 동작에 관련된 예측이 예상되는 에러는 감사로그에 남긴다.

 

일반적으로 1.3 이후부터는 java.util.logging 패키지를 사용한다.

Private final static Logger auditLogger = Logger.getLogger("request");

이와 같은 문장을 사용하여 Logger 선언한다.

-> Logger 스레드 환경에서 안전하다.

 

생성된 Logger 파일에서 log() 메소드를 활용하여 로그를 남길  있다.

Log()메소드에서 파라미터로 사용되는 로그의 레벨은 7 가지이다 심각성에 따라 설정   있다.

 

소켓 옵션

Socket 옵션은 ServerSocket 클래스가 의존하는 네이티브 소켓이 데이터를 주고받는 방법을 명시한다서버 소켓을 위해 자바는 3가지 옵션을 제공한다.

 

- SO_TIMEOUT

- SO_REUSEADDR

- SO_RCVBUF

 

SO_TIMEOUT

Public void setSoTimeout(int timeout)

Public int getSoTimeout()

SO_TIMEOUT accept() 메소드가 들어오는 연결을 대기하는 시간이다.

-> 0으로 설정된 경우 하염없이 기다린다기본값이 0이다.

 

RE_USEADDR

Public boolean getReuseAddress()

Public void setReuseAddress(boolean on)

RE_USEADDR 새로운 소켓이 바인딩하려는 포트에 대해 이전에 바인딩된 소켓으로 전송 중인 데이터가 있는 경우에도 해당 포트를 바인딩

  있는지 여부를 결정한다.

 

SO_RCVBUF

Public int getReciveBufferSize()

Public void setReceiveBufferSize()

SO_RCVBUF 옵션은 서버 소켓에 의해 수용된 클라이언트 소켓의 기본 수신 버퍼의 크기를 설정한다.

인터넷 트래픽에 따라 버퍼를 설정한다.

 

 

 

 

반응형

'JAVA > 자바 네트워크 프로그래밍' 카테고리의 다른 글

Runnable을 사용한 MD5 다이제스트 생성방법  (0) 2016.12.24
HTTP  (0) 2016.12.24
소켓 통신 (어플리케이션)  (0) 2016.12.24
UDP 통신  (0) 2016.12.24
java 소켓 통신 (보안소켓)  (0) 2016.12.24