这个AcceptEx函数用来接收一个新的连接,返回本地和远程的sockaddr_in结构地址,并接收客户端应用程序返回的第一个数据块,
相当于AcceptEx在接受客户端的连接请求之后,如果客户端连接之后直接发送了数据,那么即可以接收到这第一数据块。
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
);
sListenSocket
一个已经在监听状态下的socket句柄,用来等待客户端的连接
sAcceptSocket
一个已经创建好的socket句柄,用于客户端socket
lpOutputBuffer
一个指向buffer的指针,用来存储客户端发送来的第一组数据,还有本地和远程地址,
默认会写到指针指向的内存偏移为0的地方为起始地址,如果要保存本地或者远程的地址则这个参数必须被指定
注:如果要取得缓冲区的数据,那么大小应该是sizeof(lpOutputBuffer) - (sizeof(sockaddr_in)+16) * 2
也就是缓冲区的大小减去客户端和服务端的地址结构大小+16字节
dwReceiveDataLength
lpoutputbuffer缓冲区的大小,单位为字节数,并不用包括本地和远程地址的大小,如果此参数为0,那么AcceptEx将会
立即返回,不会等待接收到数据再返回。
dwLocalAddressLength
本地地址信息保留的字节数。该值必须至少16字节以上为使用传输协议的最大地址长度.
dwRemoteAddressLength
远程地址信息保留的字节数。该值必须至少16字节以上为使用传输协议的最大地址长度。不能为零。
lpdwBytesReceived
指向一个DWORD类型,用于接收完成后的字节数,如果投递的异步请求立即完成,则被成功更新,
如果返回ERROR_IO_PENDING并在之后完成,则这个参数的数值不确定
lpOverlapped
指向重叠结构的指针,用于处理请求。此参数必须被指定;它不能为空。
返回值
如果没有错误发生,那么AcceptEx在成功完成后返回true。
如果函数失败,那么会返回false,使用WSAGetLastError函数获取错误信息,如果WSAGetLastError函数返回ERROR_IO_PENDING
那么投递的异步操作将会在稍后完成,如果WSAGetLastError返回的错误是WSAECONNRESET,说明客户端的连接被关闭了。
注意事项
AcceptEx函数在成功调用后,执行三项任务
1、一个新的socket被接入
2、返回本地和远程的sockaddr_in结构
3、接收客户端发来的第一个数据块
注:在调用AcceptEx函数的时候,此函数是WSAIoctl函数传递SIO_GET_EXTENSION_FUNCTION_POINTER操作码,返回的一个函数指针,
WSAIoctl函数的输入缓冲区必须包含WSAID_ACCEPTEX,全局唯一标识符(GUID)的值标识调用扩展功能。成功返回,输出通过WSAIoctl函数包含一个指向AcceptEx函数。该wsaid_acceptex GUID是在mswsock h头文件定义。
可以使用 GetAcceptExSockaddrs 函数来获取本地和远程的地址以及客户端发送来的第一组数据,也可以使用getpeername函数来返回远程socket的地址,
AcceptEx其他
1、acceptex的sListenSocket必须是已经在监听的
2、acceptex的sAcceptSocket必须是已经创建好的,并且没有被使用也没有被连接的
当sAcceptSocket已经被系统接收以后,那么在工作者线程里通过GetQueuedCompletionStatus函数读取队列状态得到这个操作之后,
可以在这个sAcceptSocket套接字上使用WSASend WSARecv closesocket等操作
AcceptEx 示例代码
#include <stdio.h>
#include "winsock2.h"
#include "mswsock.h"
void main() {
//----------------------------------------
// Declare and initialize variables
WSADATA wsaData;
HANDLE hCompPort;
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
WSAOVERLAPPED olOverlap;
SOCKET ListenSocket, AcceptSocket;
sockaddr_in service;
char lpOutputBuf[1024];
int outBufLen = 1024;
DWORD dwBytes;
//----------------------------------------
// Initialize Winsock
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if( iResult != NO_ERROR )
printf("Error at WSAStartup\n");
//----------------------------------------
// Create a handle for the completion port
hCompPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, (u_long)0, 0 );
//----------------------------------------
// Create a listening socket
ListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): ListenSocket\n");
WSACleanup();
return;
}
//----------------------------------------
// 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
hostent* thisHost;
char* ip;
u_short port;
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 ) {
printf("bind failed\n");
closesocket(ListenSocket);
return;
}
//----------------------------------------
// Start listening on the listening socket
if (listen( ListenSocket, 100 ) == SOCKET_ERROR) {
printf("error listening\n");
}
printf("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.
WSAIoctl(ListenSocket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&lpfnAcceptEx,
sizeof(lpfnAcceptEx),
&dwBytes,
NULL,
NULL);
//----------------------------------------
// Create an accepting socket
AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (AcceptSocket == INVALID_SOCKET) {
printf("Error creating accept socket.\n");
WSACleanup();
return;
}
//----------------------------------------
// Empty our overlapped structure and accept connections.
memset(&olOverlap, 0, sizeof(olOverlap));
lpfnAcceptEx(ListenSocket,
AcceptSocket,
lpOutputBuf,
outBufLen - ((sizeof(sockaddr_in) + 16) * 2),
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
&dwBytes,
&olOverlap);
//----------------------------------------
// Associate the accept socket with the completion port
CreateIoCompletionPort((HANDLE)AcceptSocket, hCompPort, (u_long)0, 0);
//----------------------------------------
// Continue on to use send, recv, TransmitFile(), etc.,.
...
}