'전체 글'에 해당되는 글 678건

  1. 2011.08.13 Graceful shutdown
  2. 2011.08.13 패킷 압축 방법
  3. 2011.08.13 서브넷 계산 및 표현법, 192.168.0.0/32, CIDR
  4. 2011.08.13 [펌] IOCP를 이용한 서버 만들기
Winsock2.2에서 바뀐 것인지는 모르겠습니다만, 클라이언트가 종료될때는 2가지 경우가 있습니다.

 

1. Graceful close

   -> 클라이언트가 shutdown으로 서버에게 클라이언트의 종료를 알려준후 종료

 

2. Not graceful close

  -> 클라이언트가 shutdown을 보내지 않고 바로 closesocket을 한 경우

 

 

되도록이면 1번의 경우가 되도록 권장을 하고 있습니다. 이유는 서버에서 안정적으로 클라이언트의 접속종료를 알아내기 위해서이죠.

 

아래는 MSDN에서 발췌한 "Graceful shutdown, linger options and socket closure" 입니다.

 

Client Side Server Side
(1) Invoke shutdown(s, SD_SEND) to signal end of session and that client has no more data to send.
(2) Receive FD_CLOSE, indicating graceful shutdown in progress and that all data has been received.
(3) Send any remaining response data.
(5') Get FD_READ and invoke recv to get any response data sent by server (4) Invoke shutdown(s, SD_SEND) to indicate server has no more data to send.
(5) Receive FD_CLOSE indication (4') Invoke closesocket
(6) Invoke closesocket

 

님께서는 2번의 방법으로 클라이언트를 종료하셨는데, 이 경우 서버에서는 2가지로 종료를 확인하셔야 합니다.

 

1. recv에서 0이 리턴되는 경우(받은 데이타 사이즈가 0인경우)

2. recv에서 소켓에러가 떨어지는 경우( -1 리턴 )

 

질문에 대한 정확한 이해를 못하여서 일단 안정적인 접속 종료에 대하여 말씀드렸습니다. 도움이 되시기를 ^^

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

출처 : http://javawork.egloos.com/1813530

 

miniLZO와 zlib 두가지 압축 라이브러리로 테스트 해보았습니다.

zlib은 많이들 아실테고 miniLZO는 LZO 경량화 버젼인데 compress는 pretty fast하고 decompress는 very fast 하다는 군요. :)
아래 URL에서 받으실 수 있습니다.
miniLZO : http://www.oberhumer.com/opensource/lzo/
zlib : http://www.zlib.net/

* 간단 사용법
 - compress( unsigned char* dstBuffer, unsigned long* dstBufferLen, unsigned char* srcBuffer, unsigned long srcBufferLen )
 - decompress( unsigned char* dstBuffer, unsigned long* dstBufferLen, unsigned char* srcBuffer, unsigned long srcBufferLen )
 -  대략 이런 형식입니다.

* 버퍼 사이즈에 따른 압축율 및 압축/해제시간 비교 (size:byte, 압축/해제 시간:ms)

size

zlib com

zlib decom

zlib rate

lzo com

lzo decom

lzo rate

2046

0.28

0.06

0.58

0.10

0.007

0.92

1696

0.22

0.03

0.53

0.05

0.007

0.9

1396

0.20

0.02

0.5

0.07

0.006

0.88

1096

0.19

0.02

0.45

0.02

0.006

0.85

796

0.15

0.01

0.41

0.01

0.005

0.8

496

0.11

0.01

0.41

0.009

0.004

0.8

196

0.08

0.01

0.49

0.005

0.001

0.88

146

0.07

0.009

0.56

0.003

0.001

0.88

96

0.07

0.008

0.63

0.002

0.001

0.91

46

0.06

0.002

0.89

0.001

0

1.09



  • 압축/해제에 걸리는 시간은 miniLZO가 빠르고 압축율은 zlib이 좋습니다.
  • 대략 50 byte 이하의 패킷은 압축하면 추가 정보 기록으로 인해 오히려 크기가 커집니다. 패킷을 선택적으로 압축을 하는 기능이 필수일것  같습니다.
  • miniLZO의 경우 효율을 기대하려면 200 byte 이상은 되어야 할듯 합니다.(0.8)
  • 원래의 의도는 이동 패킷을 압축해볼 생각이었는데 보통 x/y/z를 하나의 위치로 보면 좌표값은 12 byte인데(float 3개) 이 좌표값이 16개는 들어가야 200 byte가 되니 현실성이 없을것 같습니다.
  • 처음 로그인시에 사용하는 정보들(캐릭터, 인벤토리 정보)이나 기타 자주 보내지 않는 덩치큰 패킷들(200 byte이상)을 보낼때 선택적으로 사용해야 할 듯 합니다.
  • 이전에 만든 MMORPG의 캐릭터, 인벤토리 정보 패킷을 보니 각각 200 byte 정도 되는군요.
  • 다시 테스트 해보니 50 byte 이하의 패킷은 압축을 하면 크기가 늘어나는 것은 사실이지만 100 byte 이상의 패킷은 크기와 압축율과의 상관관계는 없어 보이네요.
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

출처 : http://blog.naver.com/woodair?Redirect=Log&logNo=100025890392

 

-- CIDR 위키

http://en.wikipedia.org/wiki/CIDR

 

-- 서브넷 계산법

http://jodies.de/ipcalc

 

-- Subnet Mask Information

http://www.ripe.net/rs/ipv4/subnets.html

 

 

우선 192.168.4.0/24 는 192.168.4.0/255.255.255.0과 같습니다.

슬래쉬 뒤의 부분은 서브넷을 나타내는 것이구요. 문의하신 형태로 표현하는 것을 CIDR 포맷이라고 합니다. 아래 첨부한 테이블을 보시면 그 의미를 아실 수 있을겁니다.

 

 

Wherever in Firestarter a single numerical IP address can be inputted as part of a policy rule, a human readable hostname or a network identifier can also be used. This last form allows you to apply rules to a range of IP addresses.

 

A range is entered as either address/netmask, for example 192.168.0.1/255.255.255.0, or more commonly in CIDR format as 192.168.0.1/24.

 

The CIDR address consists of a standard dotted format 32-bit IP address and a postfix of the number of network identifying bits. This might sound confusing, and it is, but it is the only valid way to group IP addresses on the Internet.

Luckily you don't have to break out your pocket calculator to work out an IP range, as there exists many IP calculators online.

 

CIDR range notation examples
CIDR format First host Last host Number of hosts
192.168.0.1/24 192.168.0.1 192.168.0.254 254
192.168.0.1/25 196.168.0.1 192.168.0.126 126
192.168.0.1/26 192.168.0.1 192.168.0.62 62
192.168.0.1/27 192.168.0.1 192.168.0.30 30
192.168.0.1/28 192.168.0.1 192.168.0.14 15
192.168.0.9/29 192.168.0.9 192.168.0.14 6
192.168.0.10/30 192.168.0.10 192.168.0.11 2
10.0.0.0/8 10.0.0.1 10.255.255.254 16777214
10.0.1.17/28 10.0.1.17 10.0.1.30 14
 
계산해보면.. 아래와 같다..
 
11111111.11111111.11111111.00000000  1(Network) : 24
       255.        255.       255.           0   255개호스트(255-0)
11111111.11111111.11111111.10000000   1 : 25
       255.        255.        255.       128   127개호스트(255-128)
11111111.11111111.11111111.11000000   1 => 26
       255.        255.        255.       192    63개호스트(255-192)
11111111.11111111.11111111.11100000   1 => 27
       255.        255.        255.       224    31개호스트(255-224)
11111111.11111111.11111111.11110000   1 => 28
       255.        255.        255.       240   15개호스트(255-240)
11111111.11111111.11111111.11111000   1 => 29
       255.        255.        255.       248   7개호스트(255-248)
11111111.11111111.11111111.11111100   1 => 30
       255.        255.        255.       252    3개호스트(255-252)
11111111.11111111.11111111.11111110   1 => 31
       255.        255.        255.       254    1개 호스트(255-254)
 
 
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,
 

IOCP를 이용한 서버 만들기

이미 IOCP가 많이 알려진 상태이지만, 확장하기 쉽고 범용성있는 서버를 만들어 보고자, 정리차원에서 글을 써본다. 그리고 밑에 참고사이트와 자료를 링크해 놓고 각각의 소스를 비교해 보았으니 참조하기 바란다.
(본문의 내용과 관련 소스는 다른 곳에 링크 및 업로드를 삼가해 주시기 바랍니다)

I/O Completion Port ?

IOCP가 무엇인지 더욱 완벽한 설명을 원한다면,  Programming Server-Side Applications for Microsoft Windows 2000이란 책을 추천한다. 그 책에서 자세히 설명되어 있다. 

IOCP는 멀티프로세서 환경을 위해 특별히 설계된 윈도우 파일 입출력 모델중의 하나이다. 그래서 IOCP는 윈도우즈 NT계열(NT, 2000이상)에서만 유용하다는 점을 인식하고 있기 바란다.

##########0*

 개념적으로 일단 IOCP 프로그래밍을 설명한다면 그림에서와 같이 APP(프로그램)가 IOCP로 행할 포트 를 정의한 뒤,(CreateIoCompletionPort함수이용) 그 포트로 I/O (뭔가를 쓴다던지 읽는다던지)하면 그 I/O가 끝날 때까지 프로그램은 기다릴 필요가 없이 리턴되고 OS가 직접 그 I/O를 행하여서 그것이 완료 되면 그 결과값을 가지고 있다가 프로그램이 그 결과값을 물어 볼 때(GetQueuedCompletionStatus or Callback 함수이용) 알려주고 프로그램은 그에 맞은 액션을 취하면 되는 것이다. 왜 이것을 IOCP(I/O 완료 포트 : Input/Output Completion Port)라고 부르는지 이해가 됐는가?

 IOCP 소켓 프로그래밍

 위에서 간단하게 개념적인 것을 설명했지만, 본격적으로 이것을 응용한 윈도우즈 소켓 프로그래밍에 대해서 알아보자. 윈도우에서는 소켓을 화일과 마찬가지로 보기 때문에 소켓 프로그래밍에 IOCP를 사용할 수 있다. 물론 윈도우즈 소켓 프로그래밍에는 IOCP방식이외에 여러가지 방식이 있지만, 서버와 같은 대규모 유저가 접속할 가능성이 있는 프로그램을 작성할 때에는 IOCP방식을 사용해서 프로그래밍하는 것이 좋다고 한다. MS에서...

 먼저 간단하게 IOCP 소켓 프로그래밍 구조를 디자인해 보자.

1. 새로운 IOCP용 포트를 생성한다. (CreateIoCompletionPort함수이용)
2. listen용 소켓을 하나 만들고 만들어 놓은 IOCP용 포트에 추가한다. (CreateIoCompletionPort함수이용)
3
. 적당한 수의 클라이언트용 소켓을 생성하고 accept시켜 놓는다. 접속할 때마다 만들어도 된다. (accept나 AcceptEx이용)
4. 콜백함수나 쓰레드 등을 이용하여 OS의 IOCP결과값을 기다린다 (GetQueuedCompletionStatus or Callback 함수이용)
5. 결과값을 이용하여 행동한다. (ReadFile, WriteFile, WSARecv, WSASend, ...)

 아래의 참고 소스들을 보면 알겠지만은 구조는 사용 용도에 따라 세부적인 부분이라든지, 순서가 조금씩 달라지게 된다. 하지만 전체적인 관점에서 보게 된다면 서로가 위와 같은 비슷한 구조를 취하고 있다는 것을 알게 될 것이다.

 이제 위와 같은 구조를 바탕으로 확장하기 쉬운 범용성있는 서버를 한번 디자인해보자.

서버 제작

 여기서 제작하고자 하는 서버 프로그램은 아래의 참고 소스들에서 조금씩 참조되였다. 그리고 서버를  ZenServer 라고 명명지였다. (참고로 Zen은 도사라는 의미로 사용되기도 한다고 한다. ^^;)

 ZenServer (ver 0.2)의 특징은 다음과 같다.

1. IOCP, 쓰레드, 이벤트를 사용한다.
2. 사용자를 위한 클라이언트용 클래스가 존재하여 객체지향적으로 프로그램을 작성할 수 있다.
3. 확장성이 강하다.
4. 프로그램으로 가동된다.

 우선 초기버전이므로 서버의 골격만 잡아 놓았다고 해도 과언이 아니다. 하지만 확장성이 준비되어 있는 만큼 다음 버전에서는

1. 로비(= 채널 or 방) 이동, 로비 생성
2. 사용자 로긴 가능
3. 서비스로 구현

등이 가능할 것이다. (기능들은 다 준비되었지만, 코딩할 시간이 없어서.. 이번 버전에서는 제외된다.)

 ZenServer의 구조 디자인은 다음과 같다.

  • 이벤트를 하나 생성한다 . (CreateEvent)
     : ZenServer는 쓰레드로 구동되어 질 것이기 때문에 내부적으로 프로그램을 마칠 수 있는 이벤트를 하나 작성하였다.
  • 시스템 컨트롤 핸들러를 재정의한다 . (SetConsoleCtrlHandler)
     : OS에서 유저가 로그오프를 한다던지 Ctrl + C키를 눌렸을 때 ZenServer를 마치기 위해서 재정의한다. 위에서 생성한 이벤트에 메세지를 보내 프로그램을 마치게 한다. (SetEvent)
  • 새로운 IOCP용 포트를 생성한다 . (CreateIoCompletionPort)
     : CreateIoCompletionPort(
        HANDLE hFile,
        HANDLE hExistingPort,
        DWORD dwCompletionKey,
        DWORD dwConcurrentThreads);
     함수에서 hFile이 소켓(화일)을 나타내는데 OS에 새로운 입출력 완료 포트를 생성하려면 hExistingPort에 NULL값을 전달해야 한다. hFile에 INVALID_HANDLE_VALUE를 전달하면 파일에 연결되지 않은 IOCP를 생성할 수 있다. 이미 존재하는 포트에 추가로 소켓을 연결시키려면 이전에 생성한 IOCP값을  전달해야 한다.
  • 작업 쓰레드를 생성한다 (_beginthreadex)
     : 서버에서 OS가 I/O 액션이 완료된 후 결과를 알려주는 부분을 쓰레드로 작성하여 처리한다. ZenServer에서는 쓰레드를 서버가 작동되는 시스템에 따라 적당하게 생성하도록 하고 있다.

    SYSTEM_INFO si;
    GetSystemInfo( &si );
    m_MaxThread = ( int )si.dwNumberOfProcessors * 2 + 2;

     쓰레드를 많이 만든다고 해서 성능이 좋아지는 것이 아니므로 위의 코드를 잘 참조하기 바란다. 그리고 쓰레드를 생성하기 위해 CreateThread함수 대신 _beginthreadex함수를 사용한 이유는 CreateThread함수는 쓰레드에서 Win32함수만을 사용하도록 요구하고 있어서 C 런타임 라이브러리는 사용할 수 없기 때문이다. 이러한 차이점을 알고 있기 바란다.
  • 소켓을 초기화한다 (WSAStartup)
     : ZenServer는 소켓 2.2를 사용한다. 소켓 버전은 다음과 같은 OS에서 유효하다.

    1.1 : Win CE/95/98/NT/2000
    2.0 : Win 95(upgrade)/98/NT/2000
    2.2 : Win 95(upgrade)/98/NT/2000
       
  • listen용 소켓을 만든다
    : listen용 소켓을 하나 만들고 만들어 놓은 IOCP용 포트에 추가한다. (CreateIoCompletionPort)
  • 클라이언트 소켓을 만든다. (AcceptEx)
    : 소켓과 OVERLAPPED데이타를 지닌 클라이언트 클래스를 지정된 클라이언트 수만큼 만들고 OVERLAPPED데이타를 초기화하고, AcceptEx 시켜 놓는다. 여기서 accept함수를 쓰지 않고 AcceptEx를 쓰는 이유는 다음과 같다.
     accept함수는 블록(대기상태)되지만 AcceptEx는 블록되지 않고, 또한 클라이언트가 접속되지 마자 데이타를 보낸다면 그 데이타까지 처리할 수 있는 함수이기 때문이다. AcceptEx함수는 그 인자들에 대해서도 신경을 써야 한다.

    BOOL AcceptEx(
      SOCKET sListenSocket,     
      SOCKET sAcceptSocket,     
      PVOID lpOutputBuffer,     
      DWORD dwReceiveDataLength, 
      DWORD dwLocalAddressLength, 
      DWORD dwRemoteAddressLength, 
      LPDWORD lpdwBytesReceived, 
      LPOVERLAPPED lpOverlapped 
    );

     
    dwReceiveDataLength는 lpOutputBuffer의 크기를 지정하는데, 실질적으로는
    (lpOutputBuffer의 크기 - 2 * (sizeof(SOCKADDR_IN) + 16)) 이어야 한다. 사용자가 접속해서 데이타를 보낼 때 데이타와 함께 lpOutputBuffer의 뒷부분에 어드레스가 붙어서 넘어오기 때문이다. dwReceiveDataLength에 0을 지정하면 사용자는 접속했을 때 데이타를 바로 보낼 수 없고 서버가 먼저 데이타를 보내는 방식으로 처리를 해주어야 한다. 이 부분은 mvps의 소스부분에 자세히 언급되어 있다.
     OVERLAPPED(중첩)데이타는 시스템에서 선언되어 있는 스트럭처를 상속받아 사용자가 필요한 클래스나 구조체로 선언하면 된다.
  • 이벤트를 기다린다. (WaitForSingleObject)
    : 프로그램이 초반부에 선언했던 프로그램을 마칠수 있는 이벤트를 기다린다.
  • 작업 쓰레드에서 OS가 IOCP의 결과를 처리한다. (GetQueuedCompletionStatus)
    : 작업 쓰레드에서 결과를 처리하여 클라이언트 접속, 쓰기, 읽기등을 각각 처리한다.


 위와 같이 ZenServer의 구조는 간단하다(?). 이 부분은 앞으로 업그레이드가 되어도 크게 변경될 여지가 없는 곳이고 앞으로 점차 복잡해질 여지가 있는 부분은 로비부분과 클라이언트 클래스부분이다. 그렇다면 ZenServer에서 사용하고 있는 클라이언트 클래스는 어떻게 설계되었는지 설명하겠다.

클라이언트 클래스 (CClient)

 CNetPrtc
     |
   CIOCPPrtc
       |
      CClientIOCP
         |
        CClientIOCPEx
           |
          CClient

 서버 프로그램에서 접속해오는 사용자를 관리하는 일을 하는 클라이언트 클래스 CClient는 위와 같이 상속되어진 클래스이다. 왜 이렇게 많이 상속받는냐고 물어본다면 확장성때문이라고 필자는 이야기하고 싶다. 그럼 차근 차근 각클래스들이 어떤 할 일(역할이라는 말이 일본식 한자말이라 하더군요. ㅡㅡ;)을 하는지 보도록 하자.

 CNetPrtc는 가장 기본이 되는 네트워크 클래스로 모든 네트워크 통신 클래스의 공통사항이 담겨져 있다. 그래서 멤버에는 기본적인 소켓데이타밖에 존재하지 않는다. 이것을 상속받아 IPX나 TCP/IP, IOCP클래스들이 만들어 진다. (먼 미래를 바라보는 포석이라고 할까? ^^;)
 CIOCPPrtc는 IOCP와 관련된 클래스의 기본이 된다. IOCP관련된 서버 소켓과 IOCP관련된 클라이언트 소켓이 하는 일이 다르므로 우선 기본이 되는 클래스가 필요하게 되어 생성하였다. 
 CClientIOCP는 CIOCPPrtc를 상속받아 클라이언트에서만 사용되는 IOCP관련 클래스이다. 이 클래스에서는 읽기용 OVERLAPPED 데이타와 쓰기용 OVERLAPPED 데이타, 큐가 각각 존재하게 된다. (큐에는 읽어온 데이타나 보내고자하는 데이타를 저장하는 장소로 사용된다. 여기서 데이타를 구분하기 위해 데이타끝에 구분자를 넣는 방식을 사용하고 있는데, 이 방식은 나중에 TCPIP로 클라이언트 프로그램을 만들 때도 역시 유용하다) CClientIOCP는 일반적인 용도에 쓰기에 가장 유용한 클래스이므로 이 클래스를 상속받아 사용자가 자유스럽게 사용해도 된다.
 하지만 필자는 여기서 이것을 상속받은 CClientIOCPEx라는 클래스를 하나 더 만들었는데, 이것은 보다 더 쉽게 클라이언트를 제어할 클래스를 만들고자 생성한 것이다.
 여기서 필자 나름대로 서버와 클라이언트가 통신할 때 사용할 통신 데이타 프로토콜을 정의하였는데(채팅만을 사용할 서버가 아니므로), 그 형식은 다음과 같다.

ID (데이타를 정의하는 ID로 16진수형 두바이트) + 데이타

 지금 현재 정의된 것은  A0 (채팅데이타 명령) 밖에는 없지만, 나중에 추가될 예정이다.(사용할 수 있는 명령은 현재 00 ~ FF까지이다) 어쨌든 CClientIOCPEx에서는 전송받은 데이타를 분석하여 명령을 추출한 뒤 해당 함수, 즉 명령이 A0이라면 CClientIOCPEx에 있는 On0xA0함수를 직접 호출하여 준다. 또한 이러한 명령관련 함수가 모두 가상함수로 선언되어 있기 때문에 CClientIOCPEx를 상속하면 해당명령관련 함수만 재정의하여 사용하면 되므로 필자는 이를 상속한 CClient라는 클래스를 만들게 되었다.

 이제까지 장황하게 설명을 했는데, 잘 이해가 되었는지 모르겠다. 여러분이 소스를 보면 더 이해가 잘 갈지는 모르겠지만, 여기서 설명한 내용이 거의 주가 되므로 잘 이해하기 바란다.

 테스트용 클라이언트 프로그램은 전에 필요에 따라 만들었던 것을 조금 개조하여 사용했다. 클라이언트 프로그램소스에서는 위의 CNetPrtc를 계승한 CTcpPrtc이 있으므로 참조해 보기 바란다. 그럼 다음 버전의 ZenServer를 기약하면서...

ZenServer 0.2 다운로드  

 

참고 자료 :

1. 프로그램세계 (2002/03 노규남 - 윈도우용 IOCP 채팅서버 만들기)

    소스 다운로드

: 가장 간단한 구조의 서버 소스이다. 그래서 IOCP의 기초를 파악하는데 가장 쉬운 편이지만, 소스를 활용할 수는 없는 편이다.  

2. Multithreading Applications in Win32 (서적)

    소스다운로드

: 그 다음으로 간단한 구조의 에코서버 소스이다. 위보다는 조금 더 실용적이라고 할 수 있다.

2.  CodeProject (Writing scalable server applications using IOCP)

    소스 다운로드

: 실용적으로 IOCP 서버를 구성하기할 때 필요한 요소를 작성하였다. 다만, 소스만 나열되어 있어 사용자가 프로젝트를 생성해서 야 된다.

3. MVPS (Sockets, IOCPs, AcceptEx)

    소스 다운로드

: 2번 서버소스와 구조와 비슷하다. 소스를 조금만 다듬는 다면 활용할 수 있는 수준이다.

4. MSDN (Writing Scalable Application for Windows NT)

    소스 다운로드

: 조금 다른 구조로 서버를 구성하고 있는 소스이다. MSDN에서 나온 소스인 만큼 참조해야 할 소스이다.

5. 임창하 (IOCP - W2K용 서버와 NT용 서버)

    소스 다운로드

: 구조를 객체 지향적으로 만들려고 노력한 소스이다. 그래서 IOCP를 이해하는데에는 다소 좋지 않지만(?) 소스의 활용도는 높은 편이다. 그리고 W2K전용과 NT이상용을 구분하여 작성한 점이 눈에 띈다.

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,