windows网络编程WSAEventSelect模型 两个问题

时间:2022-04-28 00:09:14
《Windows网络编程》第八章中,讲到:程序没有考虑两个问题,一是不能无条件的调用accept,因为我们支持的并发连接数有限。解决方法是将套接字按MAXIMUM_WAIT_OBJECTS分组,每MAXIMUM_WAIT_OBJECTS个套接字一组,每一组分配一个工作者线程;或者采用WSAAccept代替accept,并回调自己定义的Condition Function。第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%。

新手,请问大家,1:WSAAccept代替accept,并回调自己定义的Condition Function,这个Condition Function怎么搞?
2:连接数是0时,怎么解决cpu100%的情况?
谢谢

14 个解决方案

#1


1.WSAAccept,支持回调函数,在接收到connect请求时,会交给回调函数处理
SOCKET WSAAccept(
  __in          SOCKET s,
  __out         struct sockaddr* addr,
  __in_out      LPINT addrlen,
  __in          LPCONDITIONPROC lpfnCondition, //回调函数
  __in          DWORD dwCallbackData           //回调数据
);

#2


第一个问题,可以参看msdn,有一个相应回调函数的例子

第二个问题,是针对windows网络编程这本书的一个事件模型的例子来说的。



DWORD WINAPI WorkerThread(LPVOID lpParam)
{
  int              ret, index;
  WSANETWORKEVENTS NetworkEvents;
  char             szMessage[MSGSIZE];

  while (TRUE)
  {
    ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
    if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
    {
      continue;
    }

    index = ret - WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);

    if (NetworkEvents.lNetworkEvents & FD_READ)
    {
      // Receive message from client
      ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
      if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
      {
        Cleanup(index);
      }
      else
      {
        szMessage[ret] = '\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
      }
    }

    if (NetworkEvents.lNetworkEvents & FD_CLOSE)
  {
   Cleanup(index);
  }
  }
  return 0;
}


第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%
这个意思是说,如果程序在连接数为0的情况下,没有任何要等待的网络事件,wait就会立刻返回,这个循环也会立即的,没有任何sleep的持续下去,占用所有的cpu时间

#3


引用 2 楼 vieri_ch 的回复:
第一个问题,可以参看msdn,有一个相应回调函数的例子

第二个问题,是针对windows网络编程这本书的一个事件模型的例子来说的。


C/C++ code
DWORD WINAPI WorkerThread(LPVOID lpParam)
{int              ret, index;
  WSANETWORKEVENTS NetworkEvents;char             szMessage[MSGSIZE];while (TRUE)
  {
    ret= WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE,1000, FALSE);if (ret== WSA_WAIT_FAILED|| ret== WSA_WAIT_TIMEOUT)
    {continue;
    }

    index= ret- WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index],&NetworkEvents);if (NetworkEvents.lNetworkEvents& FD_READ)
    {// Receive message from client      ret= recv(g_CliSocketArr[index], szMessage, MSGSIZE,0);if (ret==0|| (ret== SOCKET_ERROR&& WSAGetLastError()== WSAECONNRESET))
      {
        Cleanup(index);
      }else
      {
        szMessage[ret]='\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage),0);
      }
    }if (NetworkEvents.lNetworkEvents& FD_CLOSE)
  {
   Cleanup(index);
  }
  }return0;
}

第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%
这个意思是说,如果程序在连接数为0的情况下,没有任何要等待的网络事件,wait就会立刻返回,这个循环也会立即的,没有任何sleep的持续下去,占用所有的cpu时间


那具体是如何解决100%呢? 而且我的是没有设置超时1000ms, 我的直接是WSA_INFINITE。我的会一直停在WSAWaitForMultipleEvents()中,没连接数时是没返回,加不加Sleep都是cpu100%的! 怎样搞定?

#4


你就把这个例子稍微修改下啊,我也发现了在连接数为0时,WSAWaitForMultipleEvents会立即返回,接着就是执行if (ret== **)  {continue;   } 。

#5


引用 4 楼 lizhigang34 的回复:
你就把这个例子稍微修改下啊,我也发现了在连接数为0时,WSAWaitForMultipleEvents会立即返回,接着就是执行if (ret== **)  {continue;  } 。



没有设置超时,是会立即返回,我也这样试过 了,但还是会cpu100%的,就会一直continue

#6


0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED 
WSAGetLastError()=WSA_INVALID_PARAMETER

#7


引用 6 楼 stjay 的回复:
0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED
WSAGetLastError()=WSA_INVALID_PARAMETER


但我的都是作为服务端,g_iTotalConn能是0吗/?  一开始都要有个socket作为监听的,所以g_iTotalConn这时都是1了

#8


你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

#9


感觉有点奇怪,你的事件对象自动传信的还是,手动传信类型的?

#10


引用 8 楼 vieri_ch 的回复:
你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

 
对,就是这样

#11


引用 8 楼 vieri_ch 的回复:
你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

感觉有点奇怪,你的事件对象自动传信的还是,手动传信类型的?


WSAWaitForMultipleEvents 我的设置的是WSA_INFINITE 

是自动传信的

#12


引用 7 楼 johnnyfu 的回复:
引用 6 楼 stjay 的回复:
0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED
WSAGetLastError()=WSA_INVALID_PARAMETER


但我的都是作为服务端,g_iTotalConn能是0吗/?  一开始都要有个socket作为监听的,所以g_iTotalConn这时都是1了


看代码吧
#include <winsock2.h>
#include <stdio.h>
#define PORT    5150
#define MSGSIZE 1024

#pragma comment(lib, "ws2_32.lib")

int      g_iTotalConn = 0;
SOCKET   g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

DWORD WINAPI WorkerThread(LPVOID);
void Cleanup(int index);

int main()
{
WSADATA     wsaData;
SOCKET      sListen, sClient;
SOCKADDR_IN local, client;
DWORD       dwThreadId;
int         iaddrSize = sizeof(SOCKADDR_IN);

// Initialize Windows Socket library
WSAStartup(0x0202, &wsaData);

// Create listening socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Bind
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

// Listen
listen(sListen, 3);

// Create worker thread
CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);

while (TRUE)
{
    // Accept a connection
    sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
    printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

    // Associate socket with network event
    g_CliSocketArr[g_iTotalConn] = sClient;
    g_CliEventArr[g_iTotalConn] = WSACreateEvent();
    WSAEventSelect(g_CliSocketArr[g_iTotalConn],
                   g_CliEventArr[g_iTotalConn],
                   FD_READ | FD_CLOSE);
    g_iTotalConn++;
}
}

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int              ret, index;
WSANETWORKEVENTS NetworkEvents;
char             szMessage[MSGSIZE];

while (TRUE)
{
    ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
    if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
    {
      continue;
    }

    index = ret - WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);

    if (NetworkEvents.lNetworkEvents & FD_READ)
    {
      // Receive message from client
      ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
      if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
      {
        Cleanup(index);
      }
      else
      {
        szMessage[ret] = '\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
      }
    }

    if (NetworkEvents.lNetworkEvents & FD_CLOSE)
   {
    Cleanup(index);
   }
}
return 0;
}

void Cleanup(int index)
{
closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);

if (index < g_iTotalConn - 1)
{
   g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
   g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
}

g_iTotalConn--;
}


accept之前 g_iTotalConn=0

一开始确是有个socket作为监听的,但没和g_CliEventArr关联起来,
事实上g_CliEventArr和g_CliSocketArr(accept返回的socket)对应的,
和那个监听socket无关的吧

#13


刚开始时g_iTotalConn=0

客户端连上后才g_iTotalConn++;
断开后又g_iTotalConn--;
这样就又可能出现g_iTotalConn等于0的情况

#14


marks

#1


1.WSAAccept,支持回调函数,在接收到connect请求时,会交给回调函数处理
SOCKET WSAAccept(
  __in          SOCKET s,
  __out         struct sockaddr* addr,
  __in_out      LPINT addrlen,
  __in          LPCONDITIONPROC lpfnCondition, //回调函数
  __in          DWORD dwCallbackData           //回调数据
);

#2


第一个问题,可以参看msdn,有一个相应回调函数的例子

第二个问题,是针对windows网络编程这本书的一个事件模型的例子来说的。



DWORD WINAPI WorkerThread(LPVOID lpParam)
{
  int              ret, index;
  WSANETWORKEVENTS NetworkEvents;
  char             szMessage[MSGSIZE];

  while (TRUE)
  {
    ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
    if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
    {
      continue;
    }

    index = ret - WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);

    if (NetworkEvents.lNetworkEvents & FD_READ)
    {
      // Receive message from client
      ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
      if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
      {
        Cleanup(index);
      }
      else
      {
        szMessage[ret] = '\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
      }
    }

    if (NetworkEvents.lNetworkEvents & FD_CLOSE)
  {
   Cleanup(index);
  }
  }
  return 0;
}


第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%
这个意思是说,如果程序在连接数为0的情况下,没有任何要等待的网络事件,wait就会立刻返回,这个循环也会立即的,没有任何sleep的持续下去,占用所有的cpu时间

#3


引用 2 楼 vieri_ch 的回复:
第一个问题,可以参看msdn,有一个相应回调函数的例子

第二个问题,是针对windows网络编程这本书的一个事件模型的例子来说的。


C/C++ code
DWORD WINAPI WorkerThread(LPVOID lpParam)
{int              ret, index;
  WSANETWORKEVENTS NetworkEvents;char             szMessage[MSGSIZE];while (TRUE)
  {
    ret= WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE,1000, FALSE);if (ret== WSA_WAIT_FAILED|| ret== WSA_WAIT_TIMEOUT)
    {continue;
    }

    index= ret- WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index],&NetworkEvents);if (NetworkEvents.lNetworkEvents& FD_READ)
    {// Receive message from client      ret= recv(g_CliSocketArr[index], szMessage, MSGSIZE,0);if (ret==0|| (ret== SOCKET_ERROR&& WSAGetLastError()== WSAECONNRESET))
      {
        Cleanup(index);
      }else
      {
        szMessage[ret]='\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage),0);
      }
    }if (NetworkEvents.lNetworkEvents& FD_CLOSE)
  {
   Cleanup(index);
  }
  }return0;
}

第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%
这个意思是说,如果程序在连接数为0的情况下,没有任何要等待的网络事件,wait就会立刻返回,这个循环也会立即的,没有任何sleep的持续下去,占用所有的cpu时间


那具体是如何解决100%呢? 而且我的是没有设置超时1000ms, 我的直接是WSA_INFINITE。我的会一直停在WSAWaitForMultipleEvents()中,没连接数时是没返回,加不加Sleep都是cpu100%的! 怎样搞定?

#4


你就把这个例子稍微修改下啊,我也发现了在连接数为0时,WSAWaitForMultipleEvents会立即返回,接着就是执行if (ret== **)  {continue;   } 。

#5


引用 4 楼 lizhigang34 的回复:
你就把这个例子稍微修改下啊,我也发现了在连接数为0时,WSAWaitForMultipleEvents会立即返回,接着就是执行if (ret== **)  {continue;  } 。



没有设置超时,是会立即返回,我也这样试过 了,但还是会cpu100%的,就会一直continue

#6


0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED 
WSAGetLastError()=WSA_INVALID_PARAMETER

#7


引用 6 楼 stjay 的回复:
0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED
WSAGetLastError()=WSA_INVALID_PARAMETER


但我的都是作为服务端,g_iTotalConn能是0吗/?  一开始都要有个socket作为监听的,所以g_iTotalConn这时都是1了

#8


你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

#9


感觉有点奇怪,你的事件对象自动传信的还是,手动传信类型的?

#10


引用 8 楼 vieri_ch 的回复:
你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

 
对,就是这样

#11


引用 8 楼 vieri_ch 的回复:
你的意思是WSAWaitForMultipleEvents()一直没有返回,但此时cpu 100%

感觉有点奇怪,你的事件对象自动传信的还是,手动传信类型的?


WSAWaitForMultipleEvents 我的设置的是WSA_INFINITE 

是自动传信的

#12


引用 7 楼 johnnyfu 的回复:
引用 6 楼 stjay 的回复:
0连接时,g_iTotalConn = 0;
也即
WSAWaitForMultipleEvents(0, g_CliEventArr, FALSE, 1000, FALSE);
应该是一直循环ret == WSA_WAIT_FAILED
WSAGetLastError()=WSA_INVALID_PARAMETER


但我的都是作为服务端,g_iTotalConn能是0吗/?  一开始都要有个socket作为监听的,所以g_iTotalConn这时都是1了


看代码吧
#include <winsock2.h>
#include <stdio.h>
#define PORT    5150
#define MSGSIZE 1024

#pragma comment(lib, "ws2_32.lib")

int      g_iTotalConn = 0;
SOCKET   g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

DWORD WINAPI WorkerThread(LPVOID);
void Cleanup(int index);

int main()
{
WSADATA     wsaData;
SOCKET      sListen, sClient;
SOCKADDR_IN local, client;
DWORD       dwThreadId;
int         iaddrSize = sizeof(SOCKADDR_IN);

// Initialize Windows Socket library
WSAStartup(0x0202, &wsaData);

// Create listening socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Bind
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

// Listen
listen(sListen, 3);

// Create worker thread
CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);

while (TRUE)
{
    // Accept a connection
    sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
    printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

    // Associate socket with network event
    g_CliSocketArr[g_iTotalConn] = sClient;
    g_CliEventArr[g_iTotalConn] = WSACreateEvent();
    WSAEventSelect(g_CliSocketArr[g_iTotalConn],
                   g_CliEventArr[g_iTotalConn],
                   FD_READ | FD_CLOSE);
    g_iTotalConn++;
}
}

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int              ret, index;
WSANETWORKEVENTS NetworkEvents;
char             szMessage[MSGSIZE];

while (TRUE)
{
    ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
    if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
    {
      continue;
    }

    index = ret - WSA_WAIT_EVENT_0;
    WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);

    if (NetworkEvents.lNetworkEvents & FD_READ)
    {
      // Receive message from client
      ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
      if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
      {
        Cleanup(index);
      }
      else
      {
        szMessage[ret] = '\0';
        send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
      }
    }

    if (NetworkEvents.lNetworkEvents & FD_CLOSE)
   {
    Cleanup(index);
   }
}
return 0;
}

void Cleanup(int index)
{
closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);

if (index < g_iTotalConn - 1)
{
   g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
   g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
}

g_iTotalConn--;
}


accept之前 g_iTotalConn=0

一开始确是有个socket作为监听的,但没和g_CliEventArr关联起来,
事实上g_CliEventArr和g_CliSocketArr(accept返回的socket)对应的,
和那个监听socket无关的吧

#13


刚开始时g_iTotalConn=0

客户端连上后才g_iTotalConn++;
断开后又g_iTotalConn--;
这样就又可能出现g_iTotalConn等于0的情况

#14


marks