AcceptEx 함수(mswsock.h)
AcceptEx 함수는 새 연결을 허용하고 로컬 및 원격 주소를 반환하며 클라이언트 애플리케이션에서 보낸 첫 번째 데이터 블록을 받습니다.
구문
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 값이 반환됩니다.
함수가 실패하면 AcceptEx 는 FALSE를 반환합니다. 그런 다음 WSAGetLastError 함수를 호출하여 확장된 오류 정보를 반환할 수 있습니다. WSAGetLastError가 ERROR_IO_PENDING 반환하는 경우 작업이 성공적으로 시작되었으며 여전히 진행 중입니다. 오류가 WSAECONNRESET인 경우 들어오는 연결이 표시되었지만 호출을 수락하기 전에 원격 피어에 의해 종료되었습니다.
설명
AcceptEx 함수는 여러 소켓 함수를 단일 API/커널 전환으로 결합합니다. AcceptEx 함수는 성공하면 다음 세 가지 작업을 수행합니다.
- 새 연결이 허용됩니다.
- 연결에 대한 로컬 주소와 원격 주소가 모두 반환됩니다.
- 원격에서 보낸 첫 번째 데이터 블록이 수신됩니다.
프로그램은 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 전용)
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);
}
Windows Phone 8: 이 함수는 Windows Phone 8 이상에서 Windows Phone 스토어 앱에서 지원됩니다.
Windows 8.1 및 Windows 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 |