AcceptEx

 

 

在Windows NT和Windows 2000中,重叠I/O模型也允许应用程序以一种重叠方式,实现对连接的接受。具体的做法是在监听套接字上调用AcceptEx函数。AcceptEx是一个特殊的Winsock 1.1扩展函数,位于M s w s o c k . h头文件以及M s w s o c k . l i b库文件内。该函数最初的设计宗旨是在Windows NT与Windows 2000操作系统上,处理Wi n 3 2的重叠I/O机制。但事实上,它也适用于Winsock 2中的重叠I/O。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参数指定的是一个监听套接字。sAcceptSocket参数指定的是另一个套接字,负责对进入连接请求的“接受”。

AcceptEx函数和accept函数的区别在于,我们必须提供接受的套接字,而不是让函数自动为我们创建。正是由于要提供套接字,所以要求我们事先调用socket或WSASocket函数,创建一个套接字,以便通过sAcceptSocket参数,将其传递给AcceptEx。

lpOutputBufferr参数指定的是一个特殊的缓冲区,因为它要负责三种数据的接收:服务器的本地地址,客户机的远程地址,以及在新建连接上发送的第一个数据块。dwReceiveDataLengthh参数以字节为单位,指定了在lpOutputBufferr缓冲区中,保留多大的空间,用于数据的接收。如这个参数设为0,那么在连接的接受过程中,不会再一道接收任何数据。 dwLocalAddressLengthdwRemoteAddressLength参数也是以字节为单位,指定在lpOutputBuffer缓冲区中,保留多大的

空间,在一个套接字被接受的时候,用于本地和远程地址信息的保存。要注意的是,和当前采用的传送协议允许的最大地址长度比较起来,这里指定的缓冲区大小至少应多出1 6字节。举个例子来说,假定正在使用的是T C P / I P协议,那么这里的大小应设为“SOCKADDRIN结构的长度+1 6字节”。lpdwBytesReceived参数用于返回接收到的实际数据量,以字节为单位。只有在操

作以同步方式完成的前提下,才会设置这个参数。假如AcceptEx函数返回ERROR_IO_PENDING,那么这个参数永远都不会设置,我们必须利用完成事件通知机制,获知实际读取的字节量。最后一个参数是lpOverlapped,它对应的是一个OVERLAPPED结构,允许AcceptEx以一种异步方式工作。如我们早先所述,只有在一个重叠I/O应用中,该函数才需要使用事件对象通知机制,这是由于此时没有一个完成例程参数可供使用。

 

 

一个名为GetAcceptExSockaddrs 的Winsock扩展函数可从lpOutputBuffer中解析出本地和远程地址元素。GetAcceptExSockaddrs定义如下:

 

void GetAcceptExSockaddrs(
  __in          PVOID lpOutputBuffer,
  __in          DWORD dwReceiveDataLength,
  __in          DWORD dwLocalAddressLength,
  __in          DWORD dwRemoteAddressLength,
  __out         LPSOCKADDR* LocalSockaddr,
  __out         LPINT LocalSockaddrLength,
  __out         LPSOCKADDR* RemoteSockaddr,
  __out         LPINT RemoteSockaddrLength
);

lpOutputBuffer参数应设为自AcceptEx返回的lpOutputBuffer。dwReceiveDataLength、dwLocalAddressLength以及dwRemoteAddressLength参数应设为与我们传递给AcceptEx的dwReceiveDataLength、dwLocalAddressLength以及d dwRemoteAddressLength参数相同的值。 L o c a l S o c k a d d r和R e m o t e S o c k a d d r参数分别是指向一个包含了本地及远程地址信息的 S O C K A D D R结构的指针,负责从最初的l p O u t p u t B u ff e r参数接收一个指针偏移。这样一来,根据l p O u t p u t B u ff e r中包含的地址信息,我们可以非常轻松地对一个S O C K A D D R结构中的元素进行引用。L o c a l S o c k a d d r L e n g t h和R e m o t e S o c k a d d r L e n g t h参数则分别负责接收本地及远程地址的长度。 

///////////////////////////////////////////////////////////////// // 初始化Socket bool CIOCPModel::_InitializeListenSocket() { // AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针 GUID GuidAcceptEx = WSAID_ACCEPTEX; GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; // 服务器地址信息,用于绑定Socket struct sockaddr_in ServerAddress; // 生成用于监听的Socket的信息 m_pListenContext = new PER_SOCKET_CONTEXT; // 需要使用重叠IO,必须得使用WSASocket来建立Socket,才可以支持重叠IO操作 m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == m_pListenContext->m_Socket) { this->_ShowMessage("初始化Socket失败,错误代码: %d.\n", WSAGetLastError()); return false; } else { TRACE("WSASocket() 完成.\n"); } // 将Listen Socket绑定至完成端口中 if( NULL== CreateIoCompletionPort( (HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort,(DWORD)m_pListenContext, 0)) { this->_ShowMessage("绑定 Listen Socket至完成端口失败!错误代码: %d/n", WSAGetLastError()); RELEASE_SOCKET( m_pListenContext->m_Socket ); return false; } else { TRACE("Listen Socket绑定完成端口 完成.\n"); } // 填充地址信息 ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress)); ServerAddress.sin_family = AF_INET; // 这里可以绑定任何可用的IP地址,或者绑定一个指定的IP地址 //ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY); ServerAddress.sin_addr.s_addr = inet_addr(m_strIP.GetString()); ServerAddress.sin_port = htons(m_nPort); // 绑定地址和端口 if (SOCKET_ERROR == bind(m_pListenContext->m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress))) { this->_ShowMessage("bind()函数执行错误.\n"); return false; } else { TRACE("bind() 完成.\n"); } // 开始进行监听 if (SOCKET_ERROR == listen(m_pListenContext->m_Socket,SOMAXCONN)) { this->_ShowMessage("Listen()函数执行出现错误.\n"); return false; } else { TRACE("Listen() 完成.\n"); } // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数 // 所以需要额外获取一下函数的指针, // 获取AcceptEx函数指针 DWORD dwBytes = 0; if(SOCKET_ERROR == WSAIoctl
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值