Network/Windows

윈속 확장 라이브러리 AcceptEX, GetAcceptExSockaddrs, TransmitFile

Binceline 2012. 10. 23. 10:55


출처 :  http://blog.naver.com/PostView.nhn?blogId=spaciall&logNo=50100384032


----------------------------------------

1.1.1.      AcceptEx

AcceptEx는 서버가 클라이언트의 연결을 받아들일 때 비동기 호출(asynchronous call)로 동작하게 하여 다른 클라이언트의 연결을 즉시 받아들일 수 있도록 한다.

 

//////////////////////////////////////////////////////////////////////////

BOOL

PASCALFAR

AcceptEx(

        INSOCKETsListenSocket,

        INSOCKETsAcceptSocket,

        INPVOIDlpOutputBuffer,

        INDWORDdwReceiveDataLength,

        INDWORDdwLocalAddressLength,

        INDWORDdwRemoteAddressLength,

        OUTLPDWORDlpdwBytesReceived,

        INLPOVERLAPPEDlpOverlapped

);

//////////////////////////////////////////////////////////////////////////

(1)   sListenSocket: 클라이언트의 연결을 대기하고 있던 서버 소켓.

(2)   sAcceptSocket: 클라이언트의 연결을 수용할 소켓.
이 소켓은 연결되거나 바인드되지 않은 소켓이어야 한다
.
서버는 소켓을 미리 생성해 두고 있어야 한다
.
그때그때 소켓을 생성하는 것이 부담스럽다면 소켓 풀(pool of socket)을 만들어서 사용하면 된다.

 

그 뒤의4개의 파라미터는sAcceptSocket과 관계된 것들이다.

(3)   lpOutputBuffer: 클라이언트가 연결에 사용한 로컬과 리모트 어드레스와 연결되자마자 수신되는 데이터를 받을 수 있는 버퍼이다.

(4)   dwReceiveDataLength: 수신할 데이터의 크기(데이터의 크기에서 로컬과 리모트 어드레스의 크기는 제외된다). 데이터를 받을 필요가 없다면0으로 설정한다.

(5)   dwLocalAddressLength, dwRemoteAddressLength: 클라이언트가 사용한 소켓 주소 구조체의 크기에16을 더한 값이다클라이언트의 로컬 어드레스는lpOutputBuffer의 수신된 데이터의 뒤쪽에 붙는다그 뒤에 클라이언트가 사용한 리모트 어드레스가 붙는다.
dwReceiveDataLength
0이 될 수 있지만dwLocalAddressdwRemoteAddressLength0값을 가질 수 없다.

(6)   lpdwBytesReceived: 연결되자마자 받은 데이터의 크기이다.

(7)   lpOverlapped: overlapped I/O를 수행할WSAOVERLAPPED 구조체의 포인터
이 파라미터는NULL이 될 수 없는데 그 이유는overlapped I/O를 사용할 필요가 없다면AcceptEx대신에acceptWSAAccept를 사용하면 되기 때문이다.

 

//////////////////////////////////////////////////////////////////////////

// 다음은IPv4로연결을대기하는소켓에서AcceptEx를사용하는방법이다.

 

SOCKETssclient;

HANDLEhCompPort;

LPFN_ACCEPTEXlpfnAcceptExNULL;

GUIDGuidAcceptExWSAID_ACCEPTEX;

 

// WSAOVERLAPPEDPLUS 구조체는12장에서자세하게다루게되며

// WSAOVERLAPPED구조체와overlapped 작업에대한

// context information을포함하고있다.

 

WSAOVERLAPPEDPLUSol;

SOCKADDR_INsalocal;

DWORDdwBytes;

charbuf[1024];

intbuflen= 1024;

 

// completion port를생성한다.

hCompPortCreateIoCompletionPort(INVALID_HANDLE_VALUE,

                              NULL,

                              (ULONG_PTR)0,

                              0);

 

// 연결을받을소켓을생성한다.

ssocket(AF_INETSOCK_STREAMIPPROTO_TCP);

 

// 소켓을completion port에할당한다.

CreateIoCompletionPort((HANDLEs,

                       hCompPort,

                       (ULONG_PTR)0,

                       0);

 

// 소켓을5150 포트로바인드한다.

salocal.sin_familyAF_INET;

salocal.sin_porthtons(5150);

salocal.sin_addr.s_addrhtonl(INADDR_ANY);

bind(s, (SOCKADDR*) salocalsizeof(salocal));

 

// 연결을기다린다.

listen(s, 200);

 

// AcceptEx 함수를로드한다.

WSAIoctl(s,

        SIO_GET_EXTENSION_FUNCTION_POINTER,

        GuidAcceptEx,

        sizeof(GuidAcceptEx),

        lpfnAcceptEx,

        sizeof(lpfnAcceptEx),

        dwBytes,

        NULL,

        NULL);

 

// 연결을받을클라이언트소켓을생성한다.

sclientsocket(AF_INETSOCK_STREAMIPPROTO_TCP);

 

// overlapped 구조체를초기화한다.

memset(ol, 0, sizof(ol));

ol.operationOP_ACCEPTEX;

ol.clientsclient;

 

lpfnAcceptEx(s,

               sclient,

               buf,

               buflen- ((sizeof(SOCKADDR_IN) + 16) * 2),

               sizeof(SOCKADDR_IN) + 16,

               sizeof(SOCKADDR_IN) + 16,

               dwBytes,

               ol.overlapped);

 

// 완료루틴내에서GetQueueCompletionStatus 함수를호출한다.

 

// 요청한AcceptEx 작업이완료되면클라이언트소켓을

// completion port에할당한다.

//////////////////////////////////////////////////////////////////////////

이 예제는 연결을 대기하는 소켓을 초기화하는 방법과AcceptEx를 라이브러리에서 로드하는 방법을 보여주고 있다앞서 설명했지만MSWSOCK.DLL에서 확장API를 로드하는 것보다 예제에서처럼 사용하길 권장한다.

그 다음으로는overlapped I/O에 사용될 구조체를 설정하였다구조체는 다른overlapped I/O와 구별될 수 있도록 설정해야 한다.

마지막으로AcceptEx를 호출했는데 새로운 클라이언트 소켓이 생성되면completion port를 통하여 서버에 알려줄 것이다.

 

새로 생성된 클라이언트 소켓은 서버 소켓의 속성을 자동으로 상속받지 않는다따라서WSA_FLAG_OVERLAPPED와 같은 서버 소켓의 속성을 상속받게 하기 위해서는setsockopt 함수의SO_UPDATE_ACCEPT_CONTEXT 명령을 사용한다.

자세한 사항은7SO_UPDATE_ACCEPT_CONTEXT 명령을 참고.

 

AcceptEx를 사용할 때 고려해야 할 사항은dwReceiveDataLength0보다 클 경우이다.

이 경우에는 최소한 한 바이트 이상 데이터를 수신하지 못하면overlapped I/O 작업이 완료되지 않는다.때문에 클라이언트가 악의적으로 많은 수의 연결을 시도하면서 아무 데이터도 보내지 않을 경우는 문제가 될 수 있다.

7장에서는 소켓 옵션SO_CONNECT_TIME을 이용하여 이러한 사항을 피하는 방법을 배울것이다.

AcceptExWindows NT4.0이상의 버전에서 사용할 수 있다.

 

 

 

1.1.2.      GetAcceptExSockaddrs

GetAcceptExSockaddrsAcceptEx와 짝을 이루는 함수로AcceptEx를 호출하여 리턴된 버퍼(lpOutputBuffer)에서 로컬과 리모트 어드레스를 얻어오는 기능을 한다.

이 버퍼에는 클라이언트가 연결하면서 전송한 데이터와 연결에 사용한 로컬과 리모트 어드레스를 담고 있다주소는 버퍼에서 데이터의 뒤쪽에 붙어있다.

GetAcceptExSockaddrs를 호출하면 버퍼에서 적절한SOCKADDR 구조체 형식으로 주소를 변환할 수 있다.

 

//////////////////////////////////////////////////////////////////////////

VOID

PASCALFAR

GetAcceptExSockaddrs(

        INPVOIDlpOutputBuffer,

        INDWORDdwReceiveDataLength,

        INDWORDdwLocalAddressLength,

        INDWORDdwRemoteAddressLength,

        OUTstructsockaddr**LocalSockaddr,

        OUTLPINTLocalSockaddrLength,

        OUTstructsockaddr**RemoteSockaddr,

        OUTLPINTRemoteSockaddrLength

);

//////////////////////////////////////////////////////////////////////////

처음4개의 파라미터는AcceptEx에서 사용했던 것을 그대로 사용한다.

뒤의4개의 파라미터는SOCKADDR의 포인터와 구조체의 길이이다.

 

//////////////////////////////////////////////////////////////////////////

// 이전의예제에서AcceptEx호출후에

// GetAcceptExSockaddrs를사용하는방법을보여준다.

 

SOCKADDR*lpLocalSockaddrNULL, *lpRemoteSockaddrNULL;

intLocalSockaddrLen= 0, RemoteSockaddrLen= 0;

LPFN_GETACCEPTEXSOCKADDRSlpfnGetAcceptExSockaddrsNULL;

 

// GetAcceptExSockaddrs 함수를로드한다.

 

lpfnGetAcceptExSockaddrs(

        buf,

        buflen- ((sizeof(SOCKADDR_IN) + 16) * 2),

        sizeof(SOCKADDR_IN) + 16,

        sizeof(SOCKADDR_IN) + 16,

        lpLocalSockaddr,

        LocalSockaddrLen,

        lpRemoteSockaddr,

        RemoteSockaddrLen

);

//////////////////////////////////////////////////////////////////////////

호출에 성공하면lpLocalSockaddrlpRemoteSockaddr에는 소켓 주소가 담긴 구조체가 리턴된다.

 

 

 

1.1.3.      TransmitFile

TransmitFile은 연결된 소켓을 이용하여 파일을 전송하는 확장API이다.

여러분은 직접 파일을 열어서 데이터를 읽어들여 소켓에 전송하기를 반복할지아니면 열린 파일 핸들을 입력하여 커널모드에서 파일을 전송하게 할지 선택할 수 있다뒤의 방법을 사용하면 파일을 전송할 때 여러 번 커널 모드로 전환되는 것을 막을 수 있다.

 

//////////////////////////////////////////////////////////////////////////

BOOL

PASCALFAR

TransmitFile(

        INSOCKEThSocket,

        INHANDLEhFile,

        INDWORDnNumberOfBytesToWrite,

        INDWORDnNumberOfBytesPerSend,

        INLPOVERLAPPEDlpOverlapped,

        INLPTRANSMIT_FILE_BUFFERSlpTransmitBuffers,

        INDWORDdwFlags

);

//////////////////////////////////////////////////////////////////////////

(1)   hSocket: 연결된 소켓의 핸들

(2)   hFile: 전송할 파일의 핸들이 핸들은NULL이 될 수도 있는데 이런 경우에는 대신lpTransmitBuffers를 이용한다.

(3)   nNumberOfBytesToWrite: 파일로부터 전송할 양이다. 0으로 설정하면 전체를 전송한다.

(4)   nNumberOfBytesPerSend: 한번 전송할 때 전송할 블록의 크기이다. 0으로 설정하면 디폴트 크기로 전송한다. Windows NT Workstation에서는 디폴트 크기는4k이고Windows Server에서는64k이다.

(5)   lpOverlapped: 선택사항이다. lpOverlapped가 생략되면 파일은 현재 위치에서부터 파일 전송을 시작한다. lpOverlapped를 사용할 때는OVERLAPPED 구조체의offset 필드에 전송을 시작할 위치를 지정한다.

(6)   lpTransmitBuffers: TRANSMIT_FILE_BUFFERS 구조체의 포인터로 파일 전송을 시작하기 전이나 후에 전송할 데이터를 지정한다이 파라미터도 옵션이다.

(7)   dwFlags: 파일 전송과 관련된 동작방식을 정의한다.

TF_DISCONNECT

전송 작업이 완료되면 트랜스포트 레벨 접속을 끊는다.

TF_REUSE_SOCKET

전송 작업이 완료된 이후에 소켓을 재활용할 수 있도록 한다.

이 클라이언트 소켓은AcceptEx 함수에 의해 재활용될 수 있다.

이 플래그는TF_DISCONNECT와 함께 사용할 때만 유효하다.

TF_USE_DEFAULT_WORKER

시스템 디폴트 스레드를 이용해서 파일을 전송한다.

이 플래그는 큰 파일을 전송할 때 유용하다.

TF_USE_SYSTEM_THREAD

시스템 스레드를 이용해서 파일을 전송한다.

TF_USE_KERNEL_APC

전송 작업을 처리하는데 커널APC(Asynchronous Procedure Call)를 사용한다.

커널APC는 응용프로그램이 대기 상태(wait state)일 때 작동된다.

(경고성 대기 상태일 필요는 없다)

TF_WRITE_BEHIND

TransmitFile 호출이 데이터가 전송 완료되기 전에 리턴된다.

이 플래그는TF_DISCONNECTTF_REUSE_SOCKET 플래그와 함께 사용할 수 없다.

 

TransmitFile은 웹서버와 같은 파일I/O가 필요한 경우에 유용하다.

또한TransmitFileTF_DISCONNECTTF_RESUE_SOCKET와 같은 유용한 플래그를 제공하기도 한다두 플래그를 모두 설정하면 파일 전송이 끝나면 소켓의 연결을 끊고 이 소켓은AcceptExConnectEx를 사용할 때 클라이언트 소켓으로 재활용할 수 있다.

예를 들면 서버는AcceptEx를 호출할 때 사용한 소켓으로TransmitFile을 호출하여 데이터를 전송하고 전송이 끝나면 다시AcceptEx를 호출할 때 사용할 수 있다.

 

다음과 같은 방법으로 사용하는 것도 유용하다. TransmitFile에 파일 핸들(hFile)lpTransmitBuffersNULL로 설정하고 플래그로TF_DISCONNECTTF_REUSE_SOCKET을 설정한다이렇게 호출하면 아무 데이터도 전송하지 않고 소켓을AcceptEx를 호출할 때 재활용할 수 있다이렇게 하는 것은 뒤에 설명할DisconnectEx를 지원하지 않는 플랫폼에서 유용하게 사용된다.

 

TransmitFileWindowsNT4.0 이상에서 사용가능하다. TransmitFile이 서버 응용프로그램을 위하여 개발되었기 때문에 서버 버전의Windows에서만 완벽하게 동작한다.

homeprofessional 버전의Windows에서는TransmitFile(혹은TransmitPackets)은 동시에2개만이 동작한다더 이상 호출하면 먼저 호출한 작업이 끝날 때까지 작업이 큐에 쌓여 있게 된다.

 

 

 

1.1.4.      TransmitPackets

TransmitPacketsTransmitFile은 데이터를 전송하는데 이용된다는 면에서 비슷하다.

2함수의 차이는TransmitPackets는 파일과 메모리 버퍼를 둘다 보낼 수 있다는 것이다.

 

//////////////////////////////////////////////////////////////////////////

BOOL

(PASCALFARLPFN_TRANSMITPACKETS) (

        SOCKEThSocket,

        LPTRANSMIT_PACKETS_ELEMENTlpPacketArray,

        DWORDnElementCount,

        DWORDnSendSize,

        LPOVERLAPPEDlpOverlapped,

        DWORDdwFlags

);

//////////////////////////////////////////////////////////////////////////

(1)   hSocket: 데이터를 전송하길 원하는 소켓.
TransmitPacket
TransmitFile과는 달리 데이터그램과 스트림 기반의 소켓에서 모두 작동된다(TCP 소켓과UDP 소켓 모두 동작)

(2)   lpPacketArray: 하나 이상의TRANSMIT_PACKETS_ELEMENT 구조체의 배열.

(3)   nElementCount: 배열의 요소의 수.

(4)   nSendSize: TransmitFilenNumberOfBytesPerSend 파라미터와 같은 의미로 전송할 데이터의 양을 표시한다.

(5)   lpOverlapped: 옵션으로overlapped I/O 구조체를 지정한다.

(6)   dwFlags: TransmitFile에서와 비슷하다.
TransmitFile
에서TF_로 시작하는 옵션들은TransmitPackets에서는TP_로 시작한다
.
각 플래그의 의미는 동일하다

TransmitPacket
를 데이터그램에서 사용할 때는TP_DISCONNECTTP_REUSE_SOCKET을 설정하는 것은 무의미하며 에러를 발생시킨다.

 

//////////////////////////////////////////////////////////////////////////

typedefstruct_TRANSMIT_PACKETS_ELEMENT

{

        ULONGdwElFlags;

        #defineTP_ELEMENT_MEMORY   1

        #defineTP_ELEMENT_FILE     2

        #defineTP_ELEMENT_EOP      4

        ULONGcLength;

        union{

               struct{

                       LARGE_INTEGERnFileOffset;

                       HANDLE        hFile;

               };

               PVOID             pBuffer;

        };

TRANSMIT_PACKETS_ELEMENT, *PTRANSMIT_PACKETS_ELEMENT,

FAR*LPTRANSMIT_PACKETS_ELEMENT;

//////////////////////////////////////////////////////////////////////////

(1)   dwElFlags: 이 구조체가 포함하는 버퍼의 종류를 설정한다.
메모리 버퍼일 경우
: TP_ELEMENT_MEMORY
파일일 경우
: TP_ELEMENT_FILE
TP_ELEMENT_EOP: 
다른2개의 플래그와 비트OR(|)하여 사용한다하나씩 전송하는 작업에서 이 플래그는 현재 데이터를 이후의 데이터와 합치지 말 것을 표시한다이렇게 사용하면 응용프로그램에서 현재 전송 선로상의 트래픽의 상황이 어떤지 확인할 수 있다.

(2)   cLength: 파일의 메모리 버퍼로부터 얼마나 전송할지 표시한다이 구조체가 파일 핸들을 포함하고 있다면cLength의 값이0이면 전체 파일을 전송하라는 의미이다.
유니온은 메모리 버퍼의 포인터나(오프셋(offset)이 포함된파일 핸들을 가지고 있다
.
같은 파일 핸들을 여러TRANSMIT_PACKETS_ELEMENT 구조체가 참조할 수 있다이런 경우에는 어디서부터 전송할지를 파일 오프셋으로 표시한다오프셋이-1인 경우는 파일의 현재 읽기 위치에서부터 전송하라는 의미이다.

 

TransmitPackets를 사용할 때 주의해야 하는 경우는 데이터그램을 전송할 때이다. TransmitPackets를 사용할 경우에는 극도로 많은 양의 전송 요청을 처리할 수 있어서 많은 양의 데이터그램 요청이 프로토콜 드라이버에 쌓일 수 있다이때 데이터 전송의 신뢰성을 제공하지 않는 프로토콜(unreliable protocols)은 전송선에 데이터를 내보내기도 전에 시스템이 감당할 수 있을 만한 양으로 패킷을 드랍(drop)시키게 된다.

 

TransmitPacketsWindows XP이상의 버전에서만 동작한다. TransmitPacketsTransmitFile과 동일한 제약사항이 적용된다서버 버전이 아닌Windows NT에서는TransmitPackets(혹은TransmitFile)는 동시에2개만이 동작한다.

반응형

'Network > Windows' 카테고리의 다른 글

[IOCP] AcceptEx 관련  (0) 2013.01.16
[스크랩] IOCP 실행순서 보장 번역 오류!...  (0) 2013.01.14
[IOCP] 10038 Error  (1) 2012.10.06