完成例程方式实现的重叠I/O模型中,介绍中有这样的话----调用SleepEx使线程处于一种可警告的等待状态,以使得I/O完成后CompletionROUTINE可以被内核调用。如果辅助线程不调用SleepEx,则内核在完成一次I/O操作后,无法调用完成例程(因为完成例程的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内)。------我怎么都理解不了啊??为什么应该完成例程的运行和激活WSARecv异步操作的代码在同一个线程之内啊??WSAWaitForMultipleEvents的fAlertable参数到底有什么作用啊?
#include <WINSOCK2.H>
#include <stdio.h>
#define PORT    5150
#define MSGSIZE 1024
#pragma comment(lib, "ws2_32.lib")
typedef struct
{
 WSAOVERLAPPED overlap;
 WSABUF        Buffer;
  char          szMessage[MSGSIZE];
 DWORD         NumberOfBytesRecvd;
 DWORD         Flags; 
 SOCKET        sClient;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
DWORD WINAPI WorkerThread(LPVOID);
void CALLBACK CompletionROUTINE(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);
SOCKET g_sNewClientConnection;
BOOL   g_bNewConnectionArrived = FALSE;
int main()
{
  WSADATA     wsaData;
  SOCKET      sListen;
  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
    g_sNewClientConnection = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
    g_bNewConnectionArrived = TRUE;
    printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
  }
}
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
 LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
  while (TRUE)
  {
    if (g_bNewConnectionArrived)
    {
      // Launch an asynchronous operation for new arrived connection
      lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
        GetProcessHeap(),
        HEAP_ZERO_MEMORY,
        sizeof(PER_IO_OPERATION_DATA));
      lpPerIOData->Buffer.len = MSGSIZE;
      lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
      lpPerIOData->sClient = g_sNewClientConnection;
      
      WSARecv(lpPerIOData->sClient,
        &lpPerIOData->Buffer,
        1,
        &lpPerIOData->NumberOfBytesRecvd,
        &lpPerIOData->Flags,
        &lpPerIOData->overlap,
        CompletionROUTINE);      
      
      g_bNewConnectionArrived = FALSE;
    }
    SleepEx(1000, TRUE);
  }
  return 0;
}
void CALLBACK CompletionROUTINE(DWORD dwError,
                                DWORD cbTransferred,
                                LPWSAOVERLAPPED lpOverlapped,
                                DWORD dwFlags)
{
  LPPER_IO_OPERATION_DATA lpPerIOData = (LPPER_IO_OPERATION_DATA)lpOverlapped;
  
  if (dwError != 0 || cbTransferred == 0)
 {
    // Connection was closed by client
  closesocket(lpPerIOData->sClient);
  HeapFree(GetProcessHeap(), 0, lpPerIOData);
 }
  else
  {
    lpPerIOData->szMessage[cbTransferred] = '\0';
    send(lpPerIOData->sClient, lpPerIOData->szMessage, cbTransferred, 0);
    
    // Launch another asynchronous operation
    memset(&lpPerIOData->overlap, 0, sizeof(WSAOVERLAPPED));
    lpPerIOData->Buffer.len = MSGSIZE;
    lpPerIOData->Buffer.buf = lpPerIOData->szMessage;    
    WSARecv(lpPerIOData->sClient,
      &lpPerIOData->Buffer,
      1,
      &lpPerIOData->NumberOfBytesRecvd,
      &lpPerIOData->Flags,
      &lpPerIOData->overlap,
      CompletionROUTINE);
  }
}

解决方案 »

  1.   

    For the completion routine to be executed, the thread that called the I/O function must be in an alertable wait state when the completion callback function occurs. A thread goes into an alertable wait state by calling either SleepEx, MsgWaitForMultipleObjectsEx, WaitForSingleObjectEx, or WaitForMultipleObjectsEx, with the function's bAlertable parameter set to TRUE. 
      

  2.   

    以下都是个人理解
    当有网络事件时,实际上就是SleepEx调用WSAXXXX里注册的回调函数,也就是说SleepEx只会调用同一个线程里注册的完成例程
    打个比方,这台机器有两个网卡分别连在不同的交换机上,如果用一个程序来管理两个网卡的通讯,
    显然这个时候的一般就是两个线程分别处理一个网卡的通讯,我觉得这可能就是SleepEx只调用同一个线程的完成例程的原因
      

  3.   

    再顶顶,sleepex只是让挂起当前线程让当前线程处于可警告等待状态,因为要想完成例程的回调函数执行,就必须设定为可等待警告状态,可是我还是不明白那句话什么意思啊??疑惑线程执行的次序
      

  4.   

    这是线程的An asynchronous procedure call (APC)机制
    异步回调-.-下列函数可以使线程进入告警状态,这样就可以触发队列中的回调函数
    SleepEx
    WaitForSingleObjectEx
    WaitForMultipleObjectsEx
    SignalObjectAndWait
    MsgWaitForMultipleObjectsEx在这里WSARecv插入回调函数CompletionROUTINE到线程WorkerThread的APC队列中
    SleepEx后可触发回调CompletionROUTINE回调函数CompletionROUTINE是在线程WorkerThread里执行的