728x90
반응형

ServerSocket Class

  • 서버쪽에서 Socket을 생성할 때도 ,hostname 또는 IP주소와 port number가 필요하다.
  • 서버의 입장에서는 누가 connection을 요청할지 알 수 가 없어서 binding된 port를 계속해서 듣고 있어야 한다
  • 그래서 Socket Class를 사용하지 않고 ServerSocket Class를 따로 만들어야 한다. 
  • Constructors
    • public ServerSocket(int port) throws BindException, IOException
    • public ServerSocket(int port, int queueLength) throws BindException, IOException
      • 서버는 동시에 여러 Client로부터 request를 받을 수 있기 때문에 동시에 처리를 못하니까 queue에다가 request를 넣어서 처리한다. queueLength는 queue의 길이를 의미한다.
    • public ServerSocket(int port, int queueLength, InetAddress bindAddress) throws IOException
    • public ServerSocket() throws IOException  => binding 없이 socket 생성하고 binding 반드시 해주어야 함
      • public void bind(SocketAddress endpoint) throws IOException
      • public void bind(SocketAddress endpoint, int queueLength) throws IOException

예를 들어 위와 같은 코드를 입력하면 지금 현재 내 컴퓨터에서 사용되고 있는 port number들을 보여준다.

엄청 많은 port가 사용되고 있는데 한번 64032번에서는 어떤 프로그램이 실행되고 있는지 확인해보자.

17352 PID에서 사용되고 있는 것을 알 수 있다. 그럼 작업 관리자에서 어떤 프로그램인지 확인할 수 있다.

KOSinj.exe(?) 이런게 port 64032에서 사용되고 있다는 사실을 알 수 있다.

 

Server Program의 life Cycle

  1. ServerSocket() 생성자를 통하여 특정 port에 ServerSocket이 생성된다
  2. ServerSocket은 들어오는 connection을 accept() 메서드를 통해 듣는다.
    • accept()는 blocking 형태로 동작한다 -> accept()가 connection이 올때까지 기다리고 밑에 라인은 실행하지 않는다. 
    • client와 server가 연결되면 Socket object를 리턴한다. ( client와 Server를 연결하는 소켓이다)
  3. Socket의 getInputStream()과 getOutputStream() 메서드를 호출할 수 있다.
  4. Server와 Client는 약속된 protocol에 따라 interact한다.
  5. Server, Client close connection
  6. 서버는 다음 connection을 위해 Step2로 가고 다음 connection을 기다린다.

DaytimeServer -> DaytimeClient를 실행하여 localhost의 port 13번에서 서버가 실행되는 것을 확인한다.

 

위의 코드의 문제점은 무엇일까??

코드의 구조를 보면 connection request를 하나 받을 때까지 기다린다. 계속 기다리고 있다가 connection 들어오면 write를 해주고 다시 while문을 와서 다음 request를 기다린다.

만약에 client1번이 request를 보냈는데, 이 client가 굉장히 느린 client였다고 가정해보자. 그 다음 엄청나게 빠른 client2가 request를 보냈다고 가정해보자. 근데 아직도 client1이 아직도 request를 못받아서 client2는 계속 기다리고 있어야 한다. 

--> IO blocking 대문에 기다려야하는 시간이 길어진다. 

 

해결방법)

  • 각각의 connection마다 새로운 process를 만든다 (옛날의 UNIX에서는 이렇게 동작함)
  • 이렇게 하면 memory를 너무 많이 쓰기 때문에 각각의 connection마다 새로운 thread를 생성한다.

 

문제점  -> 앞의 방법의 문제점은??

  • 만약에 request가 굉장히 많이 들어오면 thread가 엄청나게 생긴다
  • thread로 인한 overhead로 인해서 performance가 slow down된다
  • DoS 공격에도 취약해진다. 

해결 방법)

  • Thread Pool을 사용한다
  • Thread Pool은 request들어올때마다 Thread를 생성하는 것이 아닌, Pool을 만들어서 이 thread를 이용해서 request를 해결한다.
  • Thread를 반납하고 다시 사용될 수 있도록 한다. 

이상적인 thread pool size는 프로그램이 I/O bound인지 CPU bound인지에 따라 다르다. 

Thread pool size가 너무 크면 memory가 높게 사용되기 때문에 본인의 상황에 맞게 사용하여야 한다.

그래서 보통은 다음과 같은 식에 따라 thread의 개수를 정한다.

 

isClosed and isBound

  • isClosed()
    • 만약 ServerSocket이 닫혔으면 true를 리턴한다
    • ServerSocket object가 ServerSocket()으로 생성되고, port와 아직 연결되어 있지 않으면 닫힌 것으로 생각하지 않는다 -> false를 리턴
  • isBound()
    • ServerSocket이 한번이라도 port와 binding되었으면 True를 리턴한다.

문제는 isClosed()가 true라고 해서 열려있는 것은 아니다. 그럼 ServerSocket이 open되어있다는 사실을 어떻게 알 수 있을까?

--> isBound() && !isClosed()

 

 

728x90
반응형

'백엔드 > Network Programming' 카테고리의 다른 글

Non-blocking I/O in Java  (0) 2022.12.12
Socket Programming(2) - Socket for Client  (0) 2022.11.13
Socket Programming(1) - socket이란?  (0) 2022.11.13
URL and URI's key  (2) 2022.10.15
Internet protocols and layers  (2) 2022.09.09

+ Recent posts