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
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 |
ヘッダー | mswsock.h (Mswsock.h を含む) |
Library | Mswsock.lib |
[DLL] | Mswsock.dll |