다음을 통해 공유


AcceptEx 함수(mswsock.h)

AcceptEx 함수는 새 연결을 허용하고 로컬 및 원격 주소를 반환하며 클라이언트 애플리케이션에서 보낸 첫 번째 데이터 블록을 받습니다.

참고 이 함수는 Windows 소켓 사양에 대한 Microsoft 전용 확장입니다.

 

구문

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

매개 변수

[in] sListenSocket

수신 대기 함수를 사용하여 이미 호출된 소켓을 식별하는 설명자입니다. 서버 애플리케이션이 이 소켓에서 연결 시도를 기다립니다.

[in] sAcceptSocket

들어오는 연결을 수락할 소켓을 식별하는 설명자입니다. 이 소켓은 바인딩되거나 연결되지 않아야 합니다.

[in] lpOutputBuffer

새 연결에서 전송된 데이터의 첫 번째 블록, 서버의 로컬 주소 및 클라이언트의 원격 주소를 수신하는 버퍼에 대한 포인터입니다. 수신 데이터는 오프셋 0부터 시작하여 버퍼의 첫 번째 부분에 기록되고 주소는 버퍼의 후반부에 기록됩니다. 이 매개 변수를 지정 합니다.

[in] dwReceiveDataLength

버퍼의 시작 부분에 있는 실제 수신 데이터에 사용할 lpOutputBuffer 의 바이트 수입니다. 이 크기에는 서버의 로컬 주소 크기나 클라이언트의 원격 주소가 포함되지 않아야 합니다. 출력 버퍼에 추가됩니다. dwReceiveDataLength가 0이면 연결을 수락해도 수신 작업이 발생하지 않습니다. 대신, 데이터를 기다리지 않고 연결이 도착하는 즉시 AcceptEx 가 완료됩니다.

[in] dwLocalAddressLength

로컬 주소 정보를 위해 예약된 바이트 수입니다. 이 값은 사용 중인 전송 프로토콜의 최대 주소 길이보다 16바이트 이상이어야 합니다.

[in] dwRemoteAddressLength

원격 주소 정보를 위해 예약된 바이트 수입니다. 이 값은 사용 중인 전송 프로토콜의 최대 주소 길이보다 16바이트 이상이어야 합니다. 0일 수 없습니다.

[out] lpdwBytesReceived

받은 바이트 수를 수신하는 DWORD 에 대한 포인터입니다. 이 매개 변수는 작업이 동기적으로 완료되는 경우에만 설정됩니다. ERROR_IO_PENDING 반환하고 나중에 완료된 경우 이 DWORD 는 설정되지 않으며 완료 알림 메커니즘에서 읽은 바이트 수를 가져와야 합니다.

[in] lpOverlapped

요청을 처리하는 데 사용되는 OVERLAPPED 구조체입니다. 이 매개 변수를 지정해야 합니다. NULL일 수 없습니다.

반환 값

오류가 발생하지 않으면 AcceptEx 함수가 성공적으로 완료되고 TRUE 값이 반환됩니다.

함수가 실패하면 AcceptExFALSE를 반환합니다. 그런 다음 WSAGetLastError 함수를 호출하여 확장된 오류 정보를 반환할 수 있습니다. WSAGetLastErrorERROR_IO_PENDING 반환하는 경우 작업이 성공적으로 시작되었으며 여전히 진행 중입니다. 오류가 WSAECONNRESET인 경우 들어오는 연결이 표시되었지만 호출을 수락하기 전에 원격 피어에 의해 종료되었습니다.

설명

AcceptEx 함수는 여러 소켓 함수를 단일 API/커널 전환으로 결합합니다. AcceptEx 함수는 성공하면 다음 세 가지 작업을 수행합니다.

  • 새 연결이 허용됩니다.
  • 연결에 대한 로컬 주소와 원격 주소가 모두 반환됩니다.
  • 원격에서 보낸 첫 번째 데이터 블록이 수신됩니다.
참고AcceptEx 함수에 대한 함수 포인터는 지정된 SIO_GET_EXTENSION_FUNCTION_POINTER opcode를 사용하여 WSAIoctl 함수를 호출하여 런타임에 가져와야 합니다. WSAIoctl 함수에 전달된 입력 버퍼에는 값이 AcceptEx 확장 함수를 식별하는 GUID(Globally Unique Identifier)인 WSAID_ACCEPTEX 포함되어야 합니다. 성공하면 WSAIoctl 함수에서 반환된 출력에 AcceptEx 함수에 대한 포인터가 포함됩니다. WSAID_ACCEPTEX GUID는 Mswsock.h 헤더 파일에 정의되어 있습니다.
 

프로그램은 accept 함수 대신 AcceptEx를 사용하여 소켓에 더 빠르게 연결할 수 있습니다.

단일 출력 버퍼는 데이터, 로컬 소켓 주소(서버) 및 원격 소켓 주소(클라이언트)를 받습니다.

단일 버퍼를 사용하면 성능이 향상됩니다. AcceptEx를 사용하는 경우 GetAcceptExSockaddrs 함수를 호출하여 버퍼를 세 가지 개별 부분(데이터, 로컬 소켓 주소 및 원격 소켓 주소)으로 구문 분석해야 합니다. Windows XP 이상에서는 AcceptEx 함수가 완료되고 허용된 소켓에 SO_UPDATE_ACCEPT_CONTEXT 옵션이 설정되면 getsockname 함수를 사용하여 수락된 소켓과 연결된 로컬 주소를 검색할 수도 있습니다. 마찬가지로 허용된 소켓과 연결된 원격 주소는 getpeername 함수를 사용하여 검색할 수 있습니다.

로컬 및 원격 주소의 버퍼 크기는 주소가 내부 형식으로 작성되므로 사용 중인 전송 프로토콜의 sockaddr 구조 크기보다 16바이트 더 커야 합니다. 예를 들어 sockaddr_in 크기(TCP/IP의 주소 구조)는 16바이트입니다. 따라서 로컬 및 원격 주소에 대해 32바이트 이상의 버퍼 크기를 지정해야 합니다.

AcceptEx 함수는 accept 함수와 달리 겹치는 I/O를 사용합니다. 애플리케이션에서 AcceptEx를 사용하는 경우 상대적으로 적은 수의 스레드를 사용하여 많은 수의 클라이언트를 서비스할 수 있습니다. 겹치는 모든 Windows 함수와 마찬가지로 Windows 이벤트 또는 완료 포트를 완료 알림 메커니즘으로 사용할 수 있습니다.

AcceptEx 함수와 accept 함수 간의 또 다른 주요 차이점은 AcceptEx에서 호출자에게 이미 두 개의 소켓이 있어야 한다는 것입니다.

  • 수신 대기할 소켓을 지정하는 1개입니다.
  • 연결을 수락할 소켓을 지정하는 1개입니다.

sAcceptSocket 매개 변수는 바인딩되거나 연결되지 않은 열린 소켓이어야 합니다.

GetQueuedCompletionStatus 함수 또는 GetOverlappedResult 함수의 lpNumberOfBytesTransferred 매개 변수는 요청에서 수신된 바이트 수를 나타냅니다.

이 작업이 성공적으로 완료되면 sAcceptSocket 을 다음 함수에만 전달할 수 있습니다.

ReadFile
WriteFile
send
WSASend
Recv
WSARecv
TransmitFile
closesocket
setsockopt(SO_UPDATE_ACCEPT_CONTEXT 전용)
참고 TF_DISCONNECT 플래그와 TF_REUSE_SOCKET 플래그를 모두 사용하여 TransmitFile 함수를 호출하면 지정된 소켓이 바인딩되거나 연결되지 않은 상태로 반환됩니다. 그런 다음 소켓 핸들을 sAcceptSocket 매개 변수의 AcceptEx 함수에 전달할 수 있지만 ConnectEx 함수에 소켓을 전달할 수는 없습니다.
 

AcceptEx 함수가 반환되면 소켓 sAcceptSocket은 연결된 소켓의 기본 상태입니다. 소켓 sAcceptSocket 은 소켓에 SO_UPDATE_ACCEPT_CONTEXT 설정될 때까지 sListenSocket 매개 변수와 연결된 소켓의 속성을 상속하지 않습니다. setsockopt 함수를 사용하여 sAcceptSocket을 소켓 핸들로 지정하고 sListenSocket을 옵션 값으로 지정하여 SO_UPDATE_ACCEPT_CONTEXT 옵션을 설정합니다.

예를 들면 다음과 같습니다.

//Need to #include <mswsock.h> for SO_UPDATE_ACCEPT_CONTEXT

int iResult = 0;

iResult =  setsockopt( sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, 
    (char *)&sListenSocket, sizeof(sListenSocket) );
   

수신 버퍼가 제공되면 연결이 수락되고 데이터를 읽을 때까지 겹치는 작업이 완료되지 않습니다. SO_CONNECT_TIME 옵션과 함께 getsockopt 함수를 사용하여 연결이 수락되었는지 여부를 검사. 수락된 경우 연결이 설정된 기간을 확인할 수 있습니다. 반환 값은 소켓이 연결된 시간(초)입니다. 소켓이 연결되지 않은 경우 getsockopt 는 0xFFFFFFFF 반환합니다. SO_CONNECT_TIME 옵션과 함께 겹치는 작업이 완료되었는지 여부를 검사 애플리케이션은 연결이 수락되었지만 데이터가 수신되지 않은 것을 확인할 수 있습니다. 이러한 방식으로 연결을 면밀히 조사하면 애플리케이션에서 한동안 설정된 연결에 데이터가 수신되지 않은지 여부를 확인할 수 있습니다. 수락된 소켓을 닫아 이러한 연결을 종료하는 것이 좋습니다. 그러면 AcceptEx 함수 호출이 오류와 함께 완료됩니다.

예를 들면 다음과 같습니다.


INT seconds;
INT bytes = sizeof(seconds);
int iResult = 0;

iResult = getsockopt( sAcceptSocket, SOL_SOCKET, SO_CONNECT_TIME,
                      (char *)&seconds, (PINT)&bytes );

if ( iResult != NO_ERROR ) {
    printf( "getsockopt(SO_CONNECT_TIME) failed: %u\n", WSAGetLastError( ) );
    exit(1);
}

참고 지정된 스레드에서 시작된 모든 I/O는 해당 스레드가 종료될 때 취소됩니다. 겹치는 소켓의 경우 작업이 완료되기 전에 스레드가 닫히면 보류 중인 비동기 작업이 실패할 수 있습니다. 자세한 내용은 ExitThread 를 참조하세요.
 

Windows Phone 8: 이 함수는 Windows Phone 8 이상에서 Windows Phone 스토어 앱에서 지원됩니다.

Windows 8.1Windows Server 2012 R2: 이 함수는 Windows 8.1, Windows Server 2012 R2 이상에서 Windows 스토어 앱에서 지원됩니다.

예제 코드

다음 예제에서는 겹치는 I/O 및 완료 포트를 사용하여 AcceptEx 함수를 사용합니다.
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int main()
{
    //----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    int iResult = 0;
    BOOL bRetVal = FALSE;

    HANDLE hCompPort;
    HANDLE hCompPort2;
    
    LPFN_ACCEPTEX lpfnAcceptEx = NULL;
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    WSAOVERLAPPED olOverlap;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET AcceptSocket = INVALID_SOCKET;
    sockaddr_in service;
    char lpOutputBuf[1024];
    int outBufLen = 1024;
    DWORD dwBytes;

    hostent *thisHost;
    char *ip;
    u_short port;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"Error at WSAStartup\n");
        return 1;
    }    

    // Create a handle for the completion port
    hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long) 0, 0);
    if (hCompPort == NULL) {
        wprintf(L"CreateIoCompletionPort failed with error: %u\n",
            GetLastError() );
        WSACleanup();
        return 1;
    }
            
    // Create a listening socket
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"Create of ListenSocket socket failed with error: %u\n",
            WSAGetLastError() );
        WSACleanup();
        return 1;
    }

    // Associate the listening socket with the completion port
    CreateIoCompletionPort((HANDLE) ListenSocket, hCompPort, (u_long) 0, 0);

    //----------------------------------------
    // Bind the listening socket to the local IP address
    // and port 27015
    port = 27015;
    thisHost = gethostbyname("");
    ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list);

    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr(ip);
    service.sin_port = htons(port);

    if (bind(ListenSocket, (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    //----------------------------------------
    // Start listening on the listening socket
    iResult = listen(ListenSocket, 100);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    wprintf(L"Listening on address: %s:%d\n", ip, port);

    // Load the AcceptEx function into memory using WSAIoctl.
    // The WSAIoctl function is an extension of the ioctlsocket()
    // function that can use overlapped I/O. The function's 3rd
    // through 6th parameters are input and output buffers where
    // we pass the pointer to our AcceptEx function. This is used
    // so that we can call the AcceptEx function directly, rather
    // than refer to the Mswsock.lib library.
    iResult = WSAIoctl(ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
             &GuidAcceptEx, sizeof (GuidAcceptEx), 
             &lpfnAcceptEx, sizeof (lpfnAcceptEx), 
             &dwBytes, NULL, NULL);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"WSAIoctl failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Create an accepting socket
    AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"Create accept socket failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Empty our overlapped structure and accept connections.
    memset(&olOverlap, 0, sizeof (olOverlap));

    bRetVal = lpfnAcceptEx(ListenSocket, AcceptSocket, lpOutputBuf,
                 outBufLen - ((sizeof (sockaddr_in) + 16) * 2),
                 sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, 
                 &dwBytes, &olOverlap);
    if (bRetVal == FALSE) {
        wprintf(L"AcceptEx failed with error: %u\n", WSAGetLastError());
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Associate the accept socket with the completion port
    hCompPort2 = CreateIoCompletionPort((HANDLE) AcceptSocket, hCompPort, (u_long) 0, 0); 
    // hCompPort2 should be hCompPort if this succeeds
    if (hCompPort2 == NULL) {
        wprintf(L"CreateIoCompletionPort associate failed with error: %u\n",
            GetLastError() );
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    
    // Continue on to use send, recv, TransmitFile(), etc.,.
    //...

    return 0;
}


QoS에 대한 참고 사항

TransmitFile 함수를 사용하면 파일이 전송된 후 소켓을 "연결이 끊긴 재사용 가능한" 상태로 반환하는 두 플래그(TF_DISCONNECT 또는 TF_REUSE_SOCKET)를 설정할 수 있습니다. 서비스 공급자가 파일 전송이 완료되기 전에 소켓과 연결된 서비스 품질을 즉시 삭제할 수 있으므로 서비스 품질이 요청된 소켓에서 이러한 플래그를 사용하면 안 됩니다. QoS 지원 소켓의 가장 좋은 방법은 이러한 플래그에 의존하지 않고 파일 전송이 완료될 때 closesocket 함수를 호출하는 것입니다.

ATM에 대한 참고 사항

Windows 소켓 2에서 ATM(비동기 전송 모드)을 사용할 때 연결 설정과 관련된 중요한 문제가 있습니다. 중요한 ATM 연결 설정 정보는 함수 수락 설명서의 설명 섹션을 참조하세요.

요구 사항

요구 사항
지원되는 최소 클라이언트 Windows 8.1, Windows Vista [데스크톱 앱 | UWP 앱]
지원되는 최소 서버 Windows Server 2003 [데스크톱 앱 | UWP 앱]
대상 플랫폼 Windows
헤더 mswsock.h(Mswsock.h 포함)
라이브러리 Mswsock.lib
DLL Mswsock.dll

추가 정보

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

OVERLAPPED

TransmitFile

Winsock 함수

Winsock 참조

받아들일

closesocket

getsockopt

listen

sockaddr