牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

iocp进行SOCKET通信(转载)

转载于hxzb7215191 的BLOG

当然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、完成端口是使用在这样的情况下,有成千上万的用户连接的时候,它能够
   保证性能不会降低。


 

#include  < winsock2.h >
#include 
< windows.h >
#include 
< stdio.h >

#define  PORT 5150
#define  DATA_BUFSIZE 8192

// 关键项
typedef  struct
{
   OVERLAPPED Overlapped;
   WSABUF DataBuf;
   CHAR Buffer[DATA_BUFSIZE];
   DWORD BytesSEND;
   DWORD BytesRECV;
}
 PER_IO_OPERATION_DATA,  *  LPPER_IO_OPERATION_DATA;


typedef 
struct  
{
   SOCKET Socket;
}
 PER_HANDLE_DATA,  *  LPPER_HANDLE_DATA;

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);

void  main( void )
{
   SOCKADDR_IN InternetAddr;
   SOCKET Listen;
   SOCKET Accept;
   HANDLE CompletionPort;
   SYSTEM_INFO SystemInfo;
   LPPER_HANDLE_DATA PerHandleData;
   LPPER_IO_OPERATION_DATA PerIoData;
   
int  i;
   DWORD RecvBytes;
   DWORD Flags;
   DWORD ThreadID;
   WSADATA wsaData;
   DWORD Ret;

   
if  ((Ret  =  WSAStartup( 0x0202 & wsaData))  !=   0 )
   
{
      printf(
" WSAStartup failed with error %d\n " , Ret);
      
return ;
   }


   
// 打开一个空的完成端口

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


   
//  Determine how many processors are on the system.

   GetSystemInfo(
& SystemInfo);

   
//  开启cpu个数的2倍个的线程

   
for (i  =   0 ; i  <  SystemInfo.dwNumberOfProcessors  *   2 ; i ++ )
   
{
      HANDLE ThreadHandle;

      
//  Create a server worker thread and pass the completion port to the thread.

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


      
//  Close the thread handle
      CloseHandle(ThreadHandle);
   }


   
// 打开一个服务器socket

   
if  ((Listen  =  WSASocket(AF_INET, SOCK_STREAM,  0 , NULL,  0 ,
      WSA_FLAG_OVERLAPPED)) 
==  INVALID_SOCKET)
   
{
      printf(
" WSASocket() failed with error %d\n " , WSAGetLastError());
      
return ;
   }
 

   InternetAddr.sin_family 
=  AF_INET;
   InternetAddr.sin_addr.s_addr 
=  htonl(INADDR_ANY);
   InternetAddr.sin_port 
=  htons(PORT);

   
if  (bind(Listen, (PSOCKADDR)  & InternetAddr,  sizeof (InternetAddr))  ==  SOCKET_ERROR)
   
{
      printf(
" bind() failed with error %d\n " , WSAGetLastError());
      
return ;
   }



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


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

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


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


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

      printf(
" Socket number %d connected\n " , Accept);
      PerHandleData
-> Socket  =  Accept;

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


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

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


      ZeroMemory(
& (PerIoData -> Overlapped),  sizeof (OVERLAPPED));
      PerIoData
-> BytesSEND  =   0 ;
      PerIoData
-> BytesRECV  =   0 ;
      PerIoData
-> DataBuf.len  =  DATA_BUFSIZE;
      PerIoData
-> DataBuf.buf  =  PerIoData -> Buffer;

      Flags 
=   0 ;
      
if  (WSARecv(Accept,  & (PerIoData -> DataBuf),  1 & RecvBytes,  & Flags,
         
& (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
      
{
         
if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
         
{
            printf(
" WSARecv() failed with error %d\n " , WSAGetLastError());
            
return ;
         }

      }

   }

}

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



      
// 是不是有人退出了

      
if  (BytesTransferred  ==   0 )
      
{
         printf(
" Closing socket %d\n " , PerHandleData -> Socket);

         
if  (closesocket(PerHandleData -> Socket)  ==  SOCKET_ERROR)
         
{
            printf(
" closesocket() failed with error %d\n " , WSAGetLastError());
            
return   0 ;
         }


         GlobalFree(PerHandleData);
         GlobalFree(PerIoData);
         
continue ;
      }


      
//

      
if  (PerIoData -> BytesRECV  ==   0 )
      
{
         PerIoData
-> BytesRECV  =  BytesTransferred;
         PerIoData
-> BytesSEND  =   0 ;
      }

      
else
      
{
         PerIoData
-> BytesSEND  +=  BytesTransferred;
      }


      
if  (PerIoData -> BytesRECV  >  PerIoData -> BytesSEND)
      
{

         
//  Post another WSASend() request.
         
//  Since WSASend() is not gauranteed to send all of the bytes requested,
         
//  continue posting WSASend() calls until all received bytes are sent.

         ZeroMemory(
& (PerIoData -> Overlapped),  sizeof (OVERLAPPED));

         PerIoData
-> DataBuf.buf  =  PerIoData -> Buffer  +  PerIoData -> BytesSEND;
         PerIoData
-> DataBuf.len  =  PerIoData -> BytesRECV  -  PerIoData -> BytesSEND;

         
if  (WSASend(PerHandleData -> Socket,  & (PerIoData -> DataBuf),  1 & SendBytes,  0 ,
            
& (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
         
{
            
if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
            
{
               printf(
" WSASend() failed with error %d\n " , WSAGetLastError());
               
return   0 ;
            }

         }

      }

      
else
      
{
         PerIoData
-> BytesRECV  =   0 ;

         
//  Now that there are no more bytes to send post another WSARecv() request.

         Flags 
=   0 ;
         ZeroMemory(
& (PerIoData -> Overlapped),  sizeof (OVERLAPPED));

         PerIoData
-> DataBuf.len  =  DATA_BUFSIZE;
         PerIoData
-> DataBuf.buf  =  PerIoData -> Buffer;

         
if  (WSARecv(PerHandleData -> Socket,  & (PerIoData -> DataBuf),  1 & RecvBytes,  & Flags,
            
& (PerIoData -> Overlapped), NULL)  ==  SOCKET_ERROR)
         
{
            
if  (WSAGetLastError()  !=  ERROR_IO_PENDING)
            
{
               printf(
" WSARecv() failed with error %d\n " , WSAGetLastError());
               
return   0 ;
            }

         }

      }

   }

}

posted on 2006-07-07 12:03 杨粼波 阅读(3627) 评论(4)  编辑 收藏 引用 所属分类: 网络编程

评论

# re: iocp进行SOCKET通信(转载) 2007-03-12 14:57 lodzio

http://www.tope-pelose.lavoro-di-piede.info @X@   回复  更多评论   

# re: iocp进行SOCKET通信(转载) 2008-03-22 10:30 郝翰霖

请问大虾这代码是如何格式化的?
如何让html仍然按C风格显示?  回复  更多评论   

# re: iocp进行SOCKET通信(转载) 2009-12-29 23:32 人生如戏

您好,我正在学习这方面的知识。。。希望得到更进一步的学习。。。希望得到您的帮助。。如果有可能请您给我发一份代码到我的邮箱里面吧。注释不注释无所谓啦。。。当然有更好啦。。谢谢!x8990253@126.com  回复  更多评论   

# re: iocp进行SOCKET通信(转载) 2010-04-09 17:49 圣神之光

这里的话应该是接收到数据就传回给客户端吧?
请问如果服务器端接收到数据以后需要处理的话线程需要如何处理?
GetQueuedCompletionStatus接收到的是否是一个完整的数据呢?
最近正在学习这个,请大虾赐教
回复邮箱:jingamesave@163.com
  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理