【问题标题】:select with other objects than sockets on windows使用 Windows 上的套接字以外的其他对象进行选择
【发布时间】:2015-10-23 14:53:49
【问题描述】:

我在执行 select() 调用等待套接字 + 管道时遇到问题。

我知道已经有一些关于这方面的主题,但我已经阅读了很多东西以及它们的反面,我不知道什么是解决我问题的最佳方法。

对我来说最好的方法是使用 WaitForMultipleObjects() 监听这两个对象,但是当我尝试仅在 WSAEvent 对象上调用它时,它会失败并且最后一个错误捕获是代码 6(无效句柄)。

WSAEVENT sockEvent = WSACreateEvent();
sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
HANDLE *pHandles = &sockEvent;
DWORD dwEvent = WaitForMultipleObjects(1, pHandles, FALSE, amqp_time_ms_until(deadline));
  switch (dwEvent) 
    { 
      // ghEvents[0] was signaled
      case WAIT_OBJECT_0 + 0: 
        // TODO: Perform tasks required by this event
        return AMQP_STATUS_OK;

        // ghEvents[1] was signaled
      case WAIT_OBJECT_0 + 1: 
        // TODO: Perform tasks required by this event
        return AMQP_STATUS_POLL_EXTERNAL_WAKE;

      case WAIT_TIMEOUT:
        return AMQP_STATUS_TIMEOUT;

        // Return value is invalid.
      default: 
        return AMQP_STATUS_SOCKET_ERROR; 
    }

所以 WaitForMultipleObjects 似乎不适用于 WinSocks 事件,但是我已经在网上看到了一些使用它的示例。 WSACreateEvent 文档 (https://msdn.microsoft.com/en-us/library/windows/desktop/ms741561%28v=vs.85%29.aspx) 说:

Windows Sockets 2 事件对象是 Windows 中的系统对象 环境。因此,如果 Windows 应用程序想要使用 自动重置事件而不是手动重置事件,应用程序可以 直接调用 CreateEvent 函数。

这不是说 WSAEvent 是基于常规的 windows 事件吗?如果是这种情况,为什么它不适用于 WaitForMultipleObjects ?文档说它可以处理常规事件。

感谢您的帮助。

【问题讨论】:

    标签: windows


    【解决方案1】:

    这是你的问题:

    sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
    

    您正在覆盖事件句柄! (As documented, WSAEventSelect 的返回值为 0 或 SOCKET_ERROR。它不是新的事件句柄。)

    试试类似的东西

    if (WSAEventSelect(fd, sockEvent, FD_WRITE) != 0) return SOCKET_ERROR;
    

    【讨论】:

      【解决方案2】:

      查看WSAEVENT 的声明发现WSAEVENT 只是HANDLE 的别名。这解释了您添加到帖子中的 WSACreateEvent 文档的注释。所以WSACreateEvent 只需通过调用CreateEvent(..., TRUE, FALSE, ...); 来创建手动重置事件。

      因此WSACreateEvent 返回的事件必须与WaitForMultipleObjects(..) 一起工作。

      根据您发布的代码,我看不出WaitForMultipleObjects(..) 在提供WSACreateEvent 返回的事件时应返回“无效句柄”的任何原因...

      管道可能不适用于WaitForMultipleObjects(..)。我记得很久以前遇到过问题,但我现在不记得细节了。但也许这是另一个开始挖掘的地方......

      这是我的小测试应用程序的代码,它创建了两个线程(一个事件线程发出正常事件信号,一个简单的 TCP/IP 服务器发送数据)。在主循环中,建立与服务器的连接并处理信号事件。

      #include <winsock2.h>
      #include <windows.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <conio.h>
      
      #pragma comment(lib, "Ws2_32.lib");
      
      
      #define SERVER_PORT  5000
      
      HANDLE hSomeEvent;
      HANDLE hSocketEvent;
      
      
      DWORD WINAPI eventThread(LPVOID pData)
      {
         while (1)
         {
            SleepEx(2250, FALSE);
      
            SetEvent(hSomeEvent);
         }
         return (0);
      }
      
      DWORD WINAPI serverThread(LPVOID pData)
      {
         SOCKET listener;
         struct sockaddr_in sockaddr;
         int size;
         SOCKET client;
      
      
         listener = socket(AF_INET, SOCK_STREAM, 0);
         if (listener == INVALID_SOCKET)
         {
            printf("Could not create socket : %d" , WSAGetLastError());
         }
      
         sockaddr.sin_family = AF_INET;
         sockaddr.sin_addr.s_addr = INADDR_ANY;
         sockaddr.sin_port = htons(SERVER_PORT);
         if (bind(listener, (struct sockaddr *)&sockaddr , sizeof(sockaddr)) == SOCKET_ERROR)
         {
            printf("Bind failed with error code : %d" , WSAGetLastError());
         }
      
         listen(listener, 1);
      
         while (listener)
         {
            size = sizeof(struct sockaddr_in);
            client = accept(listener, (struct sockaddr *)&sockaddr, &size);
      
            printf("client connected\n");
      
            while (client != INVALID_SOCKET)
            {
               SleepEx(5000, FALSE);
      
               if (send(client, "hello\0", 6, 0) != 6)
               {
                  closesocket(client);
                  shutdown(client, 2);
                  client = INVALID_SOCKET;
               }
            }
      
            SetEvent(hSomeEvent);
         }
         return (0);
      }
      
      
      
      int main()
      {
         WSADATA wsaData;
         HANDLE events[2];
         DWORD result;
         SOCKET s;
         struct hostent *hp;
         struct sockaddr_in sockaddr;
         int len;
         char buff[1024 * 16];
         HANDLE *evtPtr;
      
      
         WSAStartup(MAKEWORD(2, 2), &wsaData);
      
         hSocketEvent = WSACreateEvent();
         //hSocketEvent = CreateEvent(NULL, FALSE, FALSE, "socket_event");
         hSomeEvent = CreateEvent(NULL, FALSE, FALSE, "some_event");
      
      
         CreateThread(NULL, 0, eventThread, NULL, 0, &result);
         CreateThread(NULL, 0, serverThread, NULL, 0, &result);
      
      
         s = socket(AF_INET, SOCK_STREAM, 0);
         if (s == INVALID_SOCKET)
         {
            printf("Could not create socket : %d" , WSAGetLastError());
         }
      
         hp = gethostbyname("127.0.0.1");
         sockaddr.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
         sockaddr.sin_family = AF_INET;
         sockaddr.sin_port = htons(SERVER_PORT);
         if (connect(s, (struct sockaddr*)&sockaddr, sizeof(sockaddr)))
         {
             closesocket(s);
             printf("Could not connect socket : %d" , WSAGetLastError());
         }
      
         WSAEventSelect(s, hSocketEvent, FD_READ);
      
         do
         {
            //events[0] = hSocketEvent;
            //events[1] = hSomeEvent;
            //result = WaitForMultipleObjects(2, events, FALSE, 1000);
      
            evtPtr = &hSocketEvent;
            result = WaitForMultipleObjects(1, evtPtr, FALSE, 1000);
            switch (result)
            {
               case WAIT_OBJECT_0 + 0:
                  printf("hSocketEvent is signalled!\n");
      
                  len = recv(s, buff, sizeof(buff), 0);
                  printf("   %d bytes received\n", len);
      
                  WSAResetEvent(hSocketEvent);
                  break;
      
               case WAIT_OBJECT_0 + 1:
                  printf("hSomeEvent is signalled!\n");
                  break;
      
               case WAIT_TIMEOUT:
                  printf("timeout\n");
                  break;
      
               default:
                  printf("error = %d\n", GetLastError());
                  break;
            }
         }
         while (1);
      
         printf("\n\nend.");
         getch();
      
         return (0);
      }
      

      请注意,如果您使用WSACreateEvent,则必须在读取数据后手动重置事件(否则WaitForMultipleObjects(..) 会发疯)。

      【讨论】:

      • 事实上我在想 unix dev 并且管道只是为了从另一个线程唤醒 select() 。在 Windows 上,这只是一个简单的事件,所以最终不需要管道。但是谢谢你的帮助! :)
      猜你喜欢
      • 1970-01-01
      • 2016-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 2018-05-04
      • 1970-01-01
      • 2018-02-03
      相关资源
      最近更新 更多