AcceptEx 関数 (winsock.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
listen 関数で既に呼び出されているソケットを識別する記述子。 サーバー アプリケーションは、このソケットでの接続の試行を待機します。
[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 関数は、複数のソケット関数を 1 つの API/カーネル遷移に結合します。 AcceptEx 関数が成功すると、次の 3 つのタスクが実行されます。
- 新しい接続が受け入れられます。
- 接続のローカル アドレスとリモート アドレスの両方が返されます。
- リモートから送信されたデータの最初のブロックが受信されます。
プログラムは、accept 関数ではなく AcceptEx を使用して、ソケットへの接続をより迅速に行うことができます。
1 つの出力バッファーは、データ、ローカル ソケット アドレス (サーバー)、およびリモート ソケット アドレス (クライアント) を受け取ります。
1 つのバッファーを使用すると、パフォーマンスが向上します。 AcceptEx を使用する場合、GetAcceptExSockaddrs 関数を呼び出して、バッファーを 3 つの異なる部分 (データ、ローカル ソケット アドレス、リモート ソケット アドレス) に解析する必要があります。 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 関数のもう 1 つの重要な違いは、AcceptEx で呼び出し元に既に 2 つのソケットが必要であることです。
- リッスンするソケットを指定する 1 つ。
- 接続を受け入れるソケットを指定する 1 つ。
sAcceptSocket パラメーターは、バインドも接続もされていないオープン ソケットである必要があります。
GetQueuedCompletionStatus 関数または GetOverlappedResult 関数の lpNumberOfBytesTransferred パラメーターは、要求で受信したバイト数を示します。
この操作が正常に完了すると、 sAcceptSocket を渡すことができますが、次の関数にのみ渡すことができます。
- ReadFile
- WriteFile
- 送信
- WSASend
- Recv
- WSARecv
- TransmitFile
- closesocket
- setsockopt(SO_UPDATE_ACCEPT_CONTEXTの場合のみ)
AcceptEx 関数が戻ると、ソケット sAcceptSocket は接続されたソケットの既定の状態になります。 ソケット sAcceptSocket は、ソケットにSO_UPDATE_ACCEPT_CONTEXTが設定されるまで 、sListenSocket パラメーターに関連付けられているソケットのプロパティを継承しません。 setsockopt 関数を使用して、SO_UPDATE_ACCEPT_CONTEXT オプションを設定し、ソケット ハンドルとして sAcceptSocket を、オプション値として sListenSocket を指定します。
例:
//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の 2 つのフラグを設定して、ファイルの送信後にソケットを "切断された再利用可能な" 状態に戻すことができます。 サービス プロバイダーは、ファイル転送が完了する前にソケットに関連付けられているサービスの品質を直ちに削除できるため、これらのフラグは、サービスの品質が要求されているソケットでは使用しないでください。 QoS 対応ソケットの最適な方法は、これらのフラグに依存するのではなく、ファイル転送が完了したときに closesocket 関数を呼び出すだけです。ATM に関する注意事項
Windows ソケット 2 で非同期転送モード (ATM) を使用する場合、接続のセットアップに関連する重要な問題があります。 ATM 接続の設定に関する重要な情報については、 受け入れ 機能に関するドキュメントの「備考」セクションを参照してください。要件
要件 | 値 |
---|---|
サポートされている最小のクライアント | Windows 8.1、 Windows Vista [デスクトップ アプリ |UWP アプリ] |
サポートされている最小のサーバー | Windows Server 2003 [デスクトップ アプリのみ | UWP アプリ] |
対象プラットフォーム | Windows |
ヘッダー | winsock.h (Mswsock.h を含む) |
Library | Mswsock.lib |
[DLL] | Mswsock.dll |