Winsock WSAEventSelect()使套接字描述符不再是套接字
我正在编写一个跨平台套接字处理库(它还以一种协议无关的方式处理串行协议和一大堆其他协议。-我不是在重新发明轮子) 我需要模拟Linux轮询函数。我开始使用的代码使用select并运行良好,但无法从另一个线程中断它,因此我被迫开始使用事件对象。我最初的尝试是:Winsock WSAEventSelect()使套接字描述符不再是套接字,winsock,Winsock,我正在编写一个跨平台套接字处理库(它还以一种协议无关的方式处理串行协议和一大堆其他协议。-我不是在重新发明轮子) 我需要模拟Linux轮询函数。我开始使用的代码使用select并运行良好,但无法从另一个线程中断它,因此我被迫开始使用事件对象。我最初的尝试是: WSACreateEvent() WSAEventSelect() to associate the socket with the event object. WaitForMultipleObjectsEx() to wait on a
WSACreateEvent()
WSAEventSelect() to associate the socket with the event object.
WaitForMultipleObjectsEx() to wait on all sockets plus my interrupt event object.
select() to work out what events actually occurred on the socket.
accept()/send()/recv() to process the sockets (later and elsewhere).
这失败了accept()
声称文件描述符不是套接字。如果我注释掉了对WSAEventSelect()
的调用,本质上恢复到我以前的代码,那么一切都可以正常工作(除了我不能中断)
然后我意识到我做错了什么(根据微软的独裁统治)。我应该使用WSAEnumNetworkEvents()
,而不是使用select()
来计算每个套接字上发生了什么事件。因此,我重新编写了代码,以正确的方式执行此操作,记住在之后调用WSAEventSelect()
以解除事件对象与文件描述符的关联,这样(祈祷)accept()
现在就可以工作了
现在WSAEnumNetworkEvents()
返回一个错误,并且WSAGetLastError()
告诉我错误是WSAENOTSOCK
这是一个插座。我按照MSDN告诉我应该做的方式做事(考虑到文档的总体质量差)。但是,WSAEventSelect()
似乎导致将文件描述符标记为文件而不是套接字
我现在非常讨厌微软
以下是我的代码的精简版本:
bool do_poll(std::vector<struct pollfd> &poll_data, int timeout)
{
...
for (const auto &fd_data : poll_data) {
event_mask = 0;
if (0 != (fd_data.events & POLLIN)) {
// select() will mark a socket as readable when it closes (read size = 0) or (for
// a listen socket) when there is an incoming connection. This is the *nix paradigm.
// WSAEventSelect() hasseparate events.
event_mask |= FD_READ;
event_mask |= FD_ACCEPT;
event_mask |= FD_CLOSE;
}
if (0 != (fd_data.events & POLLOUT)) {
event_mask |= FD_WRITE;
}
event_obj = WSACreateEvent();
events.push_back(event_obj);
if (WSA_INVALID_EVENT != event_obj) {
(void)WSAEventSelect((SOCKET)fd_data.fd, event_obj, event_mask);
}
}
lock.lock();
if (WSA_INVALID_EVENT == interrupt_obj) {
interrupt_obj = WSACreateEvent();
}
if (WSA_INVALID_EVENT != interrupt_obj) {
events.push_back(interrupt_obj);
}
lock.unlock();
...
(void)WaitForMultipleObjectsEx(events.size(), &(events[0]), FALSE, dw_timeout, TRUE);
for (i = 0u; i < poll_data.size(); i++) {
if (WSA_INVALID_EVENT == events[i]) {
poll_data[i].revents |= POLLERR;
} else {
if (0 != WSAEnumNetworkEvents((SOCKET)(poll_data[i].fd), events[i], &revents)) {
poll_data[i].revents |= POLLERR;
} else {
if (0u != (revents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))) {
poll_data[i].revents |= POLLIN;
}
if (0u != (revents.lNetworkEvents & FD_WRITE)) {
poll_data[i].revents |= POLLOUT;
}
}
(void)WSAEventSelect((SOCKET)(poll_data[i].fd), NULL, 0);
(void)WSACloseEvent(event_obj);
}
}
...
}
booldo_轮询(std::vector&poll_数据,int超时)
{
...
用于(常量自动和fd_数据:轮询_数据){
事件屏蔽=0;
如果(0!=(fd_data.events和POLLIN)){
//select()将在套接字关闭时(读取大小=0)或(对于)将其标记为可读
//一个监听套接字),当有一个传入连接时。这是*nix范例。
//WSAEventSelect()已分离事件。
事件|掩码|=FD|读;
事件|掩码|=FD|U接受;
事件|掩码|=FD |关闭;
}
如果(0!=(fd_data.events&POLLOUT)){
事件|掩码|=FD|U写入;
}
event_obj=WSACreateEvent();
事件。推回(事件对象);
if(WSA\U无效\U事件!=事件\U obj){
(void)WSAEventSelect((套接字)fd_data.fd、event_obj、event_mask);
}
}
lock.lock();
如果(WSA\U无效\U事件==中断\U obj){
中断_obj=WSACreateEvent();
}
if(WSA\U无效\U事件!=中断\U obj){
事件。推回(中断对象);
}
lock.unlock();
...
(void)WaitForMultipleObjectsEx(events.size(),&(events[0]),FALSE,dw_timeout,TRUE);
对于(i=0u;i
调用WSAEventSelect((SOCKET)fd,NULL,0)
在我从works开始的代码中调用select()
之后!万岁!但是,由于wsaemnetorkevents()
需要事件对象,并且可能需要它仍然与套接字关联,这并不能解决我的问题。从MSDN文档中可以看出,您应该在套接字的生存期内关联事件对象,并且调用WSAEventSelect()
同一个套接字的第二次清除内部网络事件日志。(`基本上已损坏。我的“解决方案”是使用我的第一个选项,但在调用select()
之后,使用空指针调用WSAEventSelect()
。这是一堆热气腾腾的微软产品,但它确实有效。我不知道为什么select()
不调用WSAEventSelect()
,而accept()
和朋友却不行。我想知道为什么。我还对使用wsaemnetworkevents()
的工作示例感兴趣。我不敢相信,即使是微软的无能者也会发布一个在所有情况下都完全无法工作的API。也许是完全不正确的文档让我感到困惑。这可能会有帮助: