#include <WINSOCK2.H>
#include <stdio.h>#define PORT    5150
#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")typedef enum
{
RECV_POSTED
}OPERATION_TYPE;typedef struct
{
WSAOVERLAPPED overlap;
WSABUF         Buffer;
char           szMessage[MSGSIZE];
DWORD          NumberOfBytesRecvd;
DWORD          Flags;
OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;DWORD WINAPI WorkerThread(LPVOID);int main()
{
WSADATA                 wsaData;
SOCKET                  sListen, sClient;
SOCKADDR_IN             local, client;
DWORD                   i, dwThreadId;
int                     iaddrSize = sizeof(SOCKADDR_IN);
HANDLE                  CompletionPort = INVALID_HANDLE_VALUE;
SYSTEM_INFO             systeminfo;
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;// Initialize Windows Socket library
WSAStartup(0x0202, &wsaData);// Create completion port
CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);// Create worker thread
GetSystemInfo(&systeminfo);
for (i = 0; i < systeminfo.dwNumberOfProcessors; i++)
{
    CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
}// 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);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 the newly arrived client socket with completion port
    CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient, 0);
    
    // 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->OperationType = RECV_POSTED;
    WSARecv(sClient,
      &lpPerIOData->Buffer,
      1,
      &lpPerIOData->NumberOfBytesRecvd,
      &lpPerIOData->Flags,
      &lpPerIOData->overlap,
      NULL);
}PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
CloseHandle(CompletionPort);
closesocket(sListen);
WSACleanup();
return 0;
}DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
HANDLE                  CompletionPort=(HANDLE)CompletionPortID;
DWORD                   dwBytesTransferred;
SOCKET                  sClient;
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;while (TRUE)
{
    GetQueuedCompletionStatus(
      CompletionPort,
      &dwBytesTransferred,
      &sClient,
      (LPOVERLAPPED *)&lpPerIOData,
      INFINITE);
    if (dwBytesTransferred == 0xFFFFFFFF)
    {
      return 0;
    }
    
    if (lpPerIOData->OperationType == RECV_POSTED)
    {
      if (dwBytesTransferred == 0)
      {
        // Connection was closed by client
        closesocket(sClient);
        HeapFree(GetProcessHeap(), 0, lpPerIOData);        
      }
      else
      {
        lpPerIOData->szMessage[dwBytesTransferred] = '\0';
        send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0);
        
        // Launch another asynchronous operation for sClient
        memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
        lpPerIOData->Buffer.len = MSGSIZE;
        lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
        lpPerIOData->OperationType = RECV_POSTED;
        WSARecv(sClient,
          &lpPerIOData->Buffer,
          1,
          &lpPerIOData->NumberOfBytesRecvd,
          &lpPerIOData->Flags,
          &lpPerIOData->overlap,
          NULL);
      }
    }
}
return 0;
}本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/byxdaz/archive/2008/03/02/2139515.aspx
上面的代码是完成端口模型的简单实现代码,在主函数里有个while循环,里面有个WSARecv函数,在线程函数里也有一个,如果有客户端发送信息给服务器,是在哪个WSARecv接收到的?多谢指导。

解决方案 »

  1.   

    客户端、服务器第一次连接成功后,使用的是主线程的WSARecv进行接收,以后就都采用工作线程里面的WSARecv函数进行接收。
      

  2.   

    怎么知道,第一次使用主线程的WSARecv进行接收,以后都采用工作线程里的WSARecv函数进行接收的。
    我是认为主线程和工作线程应该都是同时在运行的么,那么客户端发送的信息到底是被哪个WSARecv接收到的?或者说主线程里的那个WSARecv函数是用来做什么用的,因为线程函数里已经有了接收到函数了啊?
    多谢指导
      

  3.   

    调试下就知道,第一次使用主线程的WSARecv进行接收,对同一个客户端来以后都采用工作线程里的WSARecv函数进行接收的
      

  4.   

    1楼是正确的.我想给出我自己的一点解释.
    如果没有主线程的WSARecv异步调用,就不会使工作线程的GetQueuedCompletionStatus函数返回.在主线程里发出一个异步接收调用,当在此套接字上第一次有数据到达并接收完毕时,将会激活等待函数GetQueuedCompletionStatus.注意一个地方:在PER_IO_OPERATION_DATA结构中,WSAOVERLAPPED结构是放在最前面的.而在主线程中,将lpPerIOData->overlap的地址传给了WSARecv函数.所以在工作线程中,GetQueuedCompletionStatus函数中返回的WSAOVERLAPPED结构的地址将是主线程传给WSARecv函数的lpPerIOData->overlap的地址.而overlap是PER_IO_OPERATION_DATA结构的第一个成员,所以GetQueuedCompletionStatus函数中返回的WSAOVERLAPPED结构的地址将是主线程中存放接收到的数据的内存地址.因此可以对数据进行处理.
    第一次以后,就是工作线程的WSARecv函数接收数据了.我没有对楼主的代码编译验证我的想法.有理解不对的地方,还请各位不吝指教.
      

  5.   

    跟着问一下:主线程里的WSARecv只接受客户端发送的第一个send内容?
      

  6.   

    这个你设置一下断点,调试一下就知道了。或者在一些特殊的地方增加printf函数,相当于做记号,看看就明白了。
      

  7.   

    都采用工作线程里面的WSARecv函数进行接收主线程里不写WSARecv可以吗?
      

  8.   

    主线程里不投递一个WSARecv,工作线程岂不是永远挂在GetQueuedCompletionStatus上等待???WSARecv只是投递一个接收请求
    真正的接收都是后台overlap完成的
    而GetQueuedCompletionStatus就是捕获这个完成事件不管你在哪里投递IO请求,最终都是工作线程的GetQueuedCompletionStatus中捕获
    假如你将所有的投递动作都放在GetQueuedCompletionStatus之后,那么它们永远不会被执行到主线程中的投递,正是为了让GetQueuedCompletionStatus跑起来
      

  9.   

    如果想在主线程消去WSARecv,有什么方法没有?
      

  10.   

    有,将监听socket也关联完成端口,不要用accept来获取用户连接,用投递AcceptEx的方式让工作线程来处理用户连入,这样就可以只在工作线程中投递WSARecv——但是,这种方法严重不推荐新手使用,这样做只会加大系统复杂度,而且不见得效率更高
      

  11.   


    投递AcceptEx我有在看
    是不合适我这样的新手使用
    复杂度太高
      

  12.   

    我大概理解了,就是主线程里的WSARecv投递时为了触发线程函数里的GetQueuedCompletionStatus从而开始接收数据,那么主线程里的WSARecv函数也有接收到数据啊,怎么说真正的接收都是在后台overlap完成的?
      

  13.   


    非也,在IOCP中,WSARecv和WSASend并不会接收或发送
    他们仅仅只是“投递”接收或发送的请求所谓的投递WSARecv,就是调用WSARecv时,传入了有效的overlapped参数,WSARecv检测到overlapped参数不为NULL,就不会进行IO操作,而会将IO操作投递到完成端口