iocp进行SOCKET通信(转载)

  1. 云栖社区>
  2. 博客>
  3. 正文

iocp进行SOCKET通信(转载)

杨粼波 2017-10-09 14:30:00 浏览922
展开阅读全文

当然TCP方式的模型还有事件选择模型。
就是把所有的网络事件和我们的一个程序里定义的事件梆定。
这个有它的好处,可能可以让我们更好的写一个线程来管理
接收与发送。
现在来讲一下一个完成端口模型。

  完成端口
  
 一个完成端口其实就是一个通知队列,由操作系统把已经完成的重叠I/O请求的通知
 放入其中。当某项I/O操作一旦完成,某个可以对该操作结果进行处理的工作者线程
 就会收到一则通知。而套接字在被创建后,可以在任何时候与某个完成端口进行关
 联。
 
 步骤:
 1、创建一个空的完成端口;
 2、得到本地机器的CPU个数;
 3、开启CPU*2个工作线程(又名线程池),全部都在等待完成端口的完成包;
 4、创建TCP的监听socket,使用事件邦定,创建监听线程;
 5、当有人连接进入的时候,将Client socket保存到一个我们自己定义的关键键,
    并把它与我们创建的完成端口关联;
 6、使用WSARecv和WSASend函数投递一些请求,这是使用重叠I/O的方式;
 7、重复5~6;

 注:1、重叠I/O的方式中,接收与发送数据包的时候,一定要进行投递请求这是
   它们这个体系结构的特点
   当然,在完成端口方式中,不是直接使用的WSARecv和WSASend函数进行请求
   的投递的。而是使用的ReadFile,Write的方式
  2、完成端口使用了系统内部的一些模型,所以我们只要按照一定的顺序调用就
   可以完成了。
  3、完成端口是使用在这样的情况下,有成千上万的用户连接的时候,它能够
   保证性能不会降低。


 

None.gif #include  < winsock2.h > 
None.gif#include  < windows.h > 
None.gif#include  < stdio.h > 
None.gif 
None.gif #define  PORT 5150 
None.gif #define  DATA_BUFSIZE 8192 
None.gif 
None.gif // 关键项 
None.gif 
typedef  struct 
ExpandedBlockStart.gif {
InBlock.gif   OVERLAPPED Overlapped;
InBlock.gif   WSABUF DataBuf;
InBlock.gif   CHAR Buffer[DATA_BUFSIZE];
InBlock.gif   DWORD BytesSEND;
InBlock.gif   DWORD BytesRECV;
ExpandedBlockEnd.gif
 PER_IO_OPERATION_DATA,  *  LPPER_IO_OPERATION_DATA;
None.gif
None.gif
None.giftypedef  struct  
ExpandedBlockStart.gif {
InBlock.gif   SOCKET Socket;
ExpandedBlockEnd.gif
 PER_HANDLE_DATA,  *  LPPER_HANDLE_DATA;
None.gif
None.gifDWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);
None.gif
None.gif void  main( void )
ExpandedBlockStart.gif {
InBlock.gif   SOCKADDR_IN InternetAddr;
InBlock.gif   SOCKET Listen;
InBlock.gif   SOCKET Accept;
InBlock.gif   HANDLE CompletionPort;
InBlock.gif   SYSTEM_INFO SystemInfo;
InBlock.gif   LPPER_HANDLE_DATA PerHandleData;
InBlock.gif   LPPER_IO_OPERATION_DATA PerIoData;
InBlock.gif    int  i;
InBlock.gif   DWORD RecvBytes;
InBlock.gif   DWORD Flags;
InBlock.gif   DWORD ThreadID;
InBlock.gif   WSADATA wsaData;
InBlock.gif   DWORD Ret;
InBlock.gif
InBlock.gif    if  ((Ret  =  WSAStartup( 0x0202 ,  & wsaData))  !=   0 )
ExpandedSubBlockStart.gif    {
InBlock.gif      printf( " WSAStartup failed with error %d\n " , Ret);
InBlock.gif       return ;
ExpandedSubBlockEnd.gif   } 

InBlock.gif 
InBlock.gif    // 打开一个空的完成端口 
InBlock.gif 

InBlock.gif    if  ((CompletionPort  =  CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,  0 ,  0 ))  ==  NULL)
ExpandedSubBlockStart.gif    {
InBlock.gif      printf(  " CreateIoCompletionPort failed with error: %d\n " , GetLastError());
InBlock.gif       return ;
ExpandedSubBlockEnd.gif   } 

InBlock.gif 
InBlock.gif    //  Determine how many processors are on the system. 
InBlock.gif 

InBlock.gif   GetSystemInfo( & SystemInfo);
InBlock.gif
InBlock.gif    //  开启cpu个数的2倍个的线程 
InBlock.gif 

InBlock.gif    for (i  =   0 ; i  <  SystemInfo.dwNumberOfProcessors  *   2 ; i ++ )
ExpandedSubBlockStart.gif    {
InBlock.gif      HANDLE ThreadHandle;
InBlock.gif
InBlock.gif       //  Create a server worker thread and pass the completion port to the thread. 
InBlock.gif 

InBlock.gif       if  ((ThreadHandle  =  CreateThread(NULL,  0 , ServerWorkerThread, CompletionPort,
InBlock.gif          0 ,  & ThreadID))  ==  NULL)
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " CreateThread() failed with error %d\n " , GetLastError());
InBlock.gif          return ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       //  Close the thread handle 
InBlock.gif 
      CloseHandle(ThreadHandle);
ExpandedSubBlockEnd.gif   } 

InBlock.gif 
InBlock.gif    // 打开一个服务器socket 
InBlock.gif 

InBlock.gif    if  ((Listen  =  WSASocket(AF_INET, SOCK_STREAM,  0 , NULL,  0 ,
InBlock.gif      WSA_FLAG_OVERLAPPED))  ==  INVALID_SOCKET)
ExpandedSubBlockStart.gif    {
InBlock.gif      printf( " WSASocket() failed with error %d\n " , WSAGetLastError());
InBlock.gif       return ;
ExpandedSubBlockEnd.gif   } 
 
InBlock.gif
InBlock.gif   InternetAddr.sin_family  =  AF_INET;
InBlock.gif   InternetAddr.sin_addr.s_addr  =  htonl(INADDR_ANY);
InBlock.gif   InternetAddr.sin_port  =  htons(PORT);
InBlock.gif
InBlock.gif    if  (bind(Listen, (PSOCKADDR)  & InternetAddr,  sizeof (InternetAddr))  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif    {
InBlock.gif      printf( " bind() failed with error %d\n " , WSAGetLastError());
InBlock.gif       return ;
ExpandedSubBlockEnd.gif   } 

InBlock.gif 
InBlock.gif 
InBlock.gif    if  (listen(Listen,  5 )  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif    {
InBlock.gif      printf( " listen() failed with error %d\n " , WSAGetLastError());
InBlock.gif       return ;
ExpandedSubBlockEnd.gif   } 

InBlock.gif 
InBlock.gif    // 开始接收从客户端来的连接 
InBlock.gif 

InBlock.gif    while (TRUE)
ExpandedSubBlockStart.gif    {
InBlock.gif       if  ((Accept  =  WSAAccept(Listen, NULL, NULL, NULL,  0 ))  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " WSAAccept() failed with error %d\n " , WSAGetLastError());
InBlock.gif          return ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       //  创建一个关键项用于保存这个客户端的信息,用户接收发送的重叠结构,
InBlock.gif      
 //  还有使用到的缓冲区 
InBlock.gif 
       if  ((PerHandleData  =  (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, 
InBlock.gif          sizeof (PER_HANDLE_DATA)))  ==  NULL)
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " GlobalAlloc() failed with error %d\n " , GetLastError());
InBlock.gif          return ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       //  Associate the accepted socket with the original completion port. 
InBlock.gif 

InBlock.gif      printf( " Socket number %d connected\n " , Accept);
InBlock.gif      PerHandleData -> Socket  =  Accept;
InBlock.gif
InBlock.gif       // 与我们的创建的那个完成端口关联起来,将关键项也与指定的一个完成端口关联 
InBlock.gif 
       if  (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
InBlock.gif          0 )  ==  NULL)
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " CreateIoCompletionPort failed with error %d\n " , GetLastError());
InBlock.gif          return ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       //  投递一次接收,由于接收都需要使用这个函数来投递一个接收的准备 
InBlock.gif 

InBlock.gif       if  ((PerIoData  =  (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,           sizeof (PER_IO_OPERATION_DATA)))  ==  NULL)
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " GlobalAlloc() failed with error %d\n " , GetLastError());
InBlock.gif          return ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif      ZeroMemory( & (PerIoData -> Overlapped),  sizeof (OVERLAPPED));
InBlock.gif      PerIoData -> BytesSEND  =   0 ;
InBlock.gif      PerIoData -> BytesRECV  =   0 ;
InBlock.gif      PerIoData -> DataBuf.len  =  DATA_BUFSIZE;
InBlock.gif      PerIoData -> DataBuf.buf  =  PerIoData -> Buffer;
InBlock.gif
InBlock.gif      Flags  =   0 ;
InBlock.gif       if  (WSARecv(Accept,  & (PerIoData -> DataBuf),  1 ,  & RecvBytes,  & Flags,
InBlock.gif          & (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif       {
InBlock.gif          if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
ExpandedSubBlockStart.gif          {
InBlock.gif            printf( " WSARecv() failed with error %d\n " , WSAGetLastError());
InBlock.gif             return ;
ExpandedSubBlockEnd.gif         } 

ExpandedSubBlockEnd.gif      } 

ExpandedSubBlockEnd.gif   } 

ExpandedBlockEnd.gif

None.gif // 工作线程 
None.gif 
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
ExpandedBlockStart.gif {
InBlock.gif   HANDLE CompletionPort  =  (HANDLE) CompletionPortID;
InBlock.gif   DWORD BytesTransferred;
InBlock.gif   LPOVERLAPPED Overlapped;
InBlock.gif   LPPER_HANDLE_DATA PerHandleData;
InBlock.gif   LPPER_IO_OPERATION_DATA PerIoData;
InBlock.gif   DWORD SendBytes, RecvBytes;
InBlock.gif   DWORD Flags;
InBlock.gif   
InBlock.gif    while (TRUE)
ExpandedSubBlockStart.gif    {
InBlock.gif       // 完成端口有消息来了 
InBlock.gif 
       if  (GetQueuedCompletionStatus(CompletionPort,  & BytesTransferred,
InBlock.gif         (LPDWORD) & PerHandleData, (LPOVERLAPPED  * )  & PerIoData, INFINITE)  ==   0 )
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " GetQueuedCompletionStatus failed with error %d\n " , GetLastError());
InBlock.gif          return   0 ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif 
InBlock.gif       // 是不是有人退出了 
InBlock.gif 

InBlock.gif       if  (BytesTransferred  ==   0 )
ExpandedSubBlockStart.gif       {
InBlock.gif         printf( " Closing socket %d\n " , PerHandleData -> Socket);
InBlock.gif
InBlock.gif          if  (closesocket(PerHandleData -> Socket)  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif          {
InBlock.gif            printf( " closesocket() failed with error %d\n " , WSAGetLastError());
InBlock.gif             return   0 ;
ExpandedSubBlockEnd.gif         } 

InBlock.gif 
InBlock.gif         GlobalFree(PerHandleData);
InBlock.gif         GlobalFree(PerIoData);
InBlock.gif          continue ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       //
InBlock.gif
 
InBlock.gif       if  (PerIoData -> BytesRECV  ==   0 )
ExpandedSubBlockStart.gif       {
InBlock.gif         PerIoData -> BytesRECV  =  BytesTransferred;
InBlock.gif         PerIoData -> BytesSEND  =   0 ;
ExpandedSubBlockEnd.gif      } 

InBlock.gif       else 
ExpandedSubBlockStart.gif        {
InBlock.gif         PerIoData -> BytesSEND  +=  BytesTransferred;
ExpandedSubBlockEnd.gif      } 

InBlock.gif 
InBlock.gif       if  (PerIoData -> BytesRECV  >  PerIoData -> BytesSEND)
ExpandedSubBlockStart.gif       {
InBlock.gif
InBlock.gif          //  Post another WSASend() request.
InBlock.gif         
 //  Since WSASend() is not gauranteed to send all of the bytes requested,
InBlock.gif         
 //  continue posting WSASend() calls until all received bytes are sent. 
InBlock.gif 

InBlock.gif         ZeroMemory( & (PerIoData -> Overlapped),  sizeof (OVERLAPPED));
InBlock.gif
InBlock.gif         PerIoData -> DataBuf.buf  =  PerIoData -> Buffer  +  PerIoData -> BytesSEND;
InBlock.gif         PerIoData -> DataBuf.len  =  PerIoData -> BytesRECV  -  PerIoData -> BytesSEND;
InBlock.gif
InBlock.gif          if  (WSASend(PerHandleData -> Socket,  & (PerIoData -> DataBuf),  1 ,  & SendBytes,  0 ,
InBlock.gif             & (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif          {
InBlock.gif             if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
ExpandedSubBlockStart.gif             {
InBlock.gif               printf( " WSASend() failed with error %d\n " , WSAGetLastError());
InBlock.gif                return   0 ;
ExpandedSubBlockEnd.gif            } 

ExpandedSubBlockEnd.gif         } 

ExpandedSubBlockEnd.gif      } 

InBlock.gif       else 
ExpandedSubBlockStart.gif        {
InBlock.gif         PerIoData -> BytesRECV  =   0 ;
InBlock.gif
InBlock.gif          //  Now that there are no more bytes to send post another WSARecv() request. 
InBlock.gif 

InBlock.gif         Flags  =   0 ;
InBlock.gif         ZeroMemory( & (PerIoData -> Overlapped),  sizeof (OVERLAPPED));
InBlock.gif
InBlock.gif         PerIoData -> DataBuf.len  =  DATA_BUFSIZE;
InBlock.gif         PerIoData -> DataBuf.buf  =  PerIoData -> Buffer;
InBlock.gif
InBlock.gif          if  (WSARecv(PerHandleData -> Socket,  & (PerIoData -> DataBuf),  1 ,  & RecvBytes,  & Flags,
InBlock.gif             & (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
ExpandedSubBlockStart.gif          {
InBlock.gif             if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
ExpandedSubBlockStart.gif             {
InBlock.gif               printf( " WSARecv() failed with error %d\n " , WSAGetLastError());
InBlock.gif                return   0 ;
ExpandedSubBlockEnd.gif            } 

ExpandedSubBlockEnd.gif         } 

ExpandedSubBlockEnd.gif      } 

ExpandedSubBlockEnd.gif   } 

ExpandedBlockEnd.gif

网友评论

登录后评论
0/500
评论
杨粼波
+ 关注