本文轉(zhuǎn)載來自CSDN博客
http://blog.csdn.net/nicholasmaxwell/archive/2006/05/18/744467.aspxhttp://blog.csdn.net/nicholasmaxwell/archive/2006/05/18/744464.aspx
另外一篇值得參考的《完成端口I/O模型編寫心得!》http://blog.csdn.net/jasonm2008/archive/2009/08/14/4441514.aspx#
#include "stdafx.h"
#include <iostream.h>
#include
#include
#include
#define PORT 5150
#define DATA_BUFSIZE 8192
typedef struct
{
OVERLAPPED OVerlapped;
WSABUF DATABuf;
CHAR Buffer[DATA_BUFSIZE];
DWORD BytesSend,BytesRecv;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;
DWORD WINAPI ServerWorkerThread(LPVOID ComlpetionPortID);
int main(int argc, char* argv[])
{
SOCKADDR_IN InternetAddr;
SOCKET Listen,Accept;
HANDLE CompetionPort;
SYSTEM_INFO SystenInfo;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIOData;
int i;
DWORD RecvBytes;
DWORD Flags;
DWORD ThreadID;
WSADATA wsadata;
DWORD Ret;
if (Ret = WSAStartup(0x2020,&wsadata) != 0)
{
printf("WSAStartup failed with error %d\n",Ret);
return 0;
}
//打開一個(gè)空的完成端口
if ((CompetionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0)) == NULL)
{
printf("CreateIoCompletionPort failed with error %d\n",GetLastError());
return 0;
}
GetSystemInfo(&SystenInfo);
// 開啟cpu個(gè)數(shù)的2倍個(gè)的線程
for (i=0; i < SystenInfo.dwNumberOfProcessors*2; i++)
{
HANDLE ThreadHandle;
//創(chuàng)建服務(wù)器工作線程,并且向線程傳送完成端口
if ((ThreadHandle = CreateThread(NULL,0,ServerWorkerThread,CompetionPort,0,&ThreadID)) == NULL)
{
printf("CreateThread failed with error %d\n" ,GetLastError());
return 0;
}
CloseHandle(ThreadHandle);
}
//打開一個(gè)服務(wù)器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 0;
}
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(PORT);
if (bind(Listen,(LPSOCKADDR)&InternetAddr,sizeof(InternetAddr)) == SOCKET_ERROR)
{
printf("bind failed with error %d\n",WSAGetLastError());
return 0;
}
if (listen(Listen,5) == SOCKET_ERROR)
{
printf("listen failed with error %d\n",WSAGetLastError());
return 0;
}
//接收連接并且分發(fā)給完成端口
while (TRUE)
{
if ((Accept = WSAAccept(Listen,NULL,NULL,NULL,0)) == SOCKET_ERROR)
{
printf("WSAAccept failed with error %d\n",WSAGetLastError());
return 0;
}
//創(chuàng)建與套接字相關(guān)的套接字信息結(jié)構(gòu)
if ((PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc failed with error %d\n",GetLastError());
return 0;
}
// Associate the accepted socket with the original completion port.
printf("Socket number %d connected\n",Accept);
PerHandleData->Socket = Accept;//結(jié)構(gòu)中存入接收的套接字
//與我們的創(chuàng)建的那個(gè)完成端口關(guān)聯(lián)起來,將關(guān)鍵項(xiàng)也與指定的一個(gè)完成端口關(guān)聯(lián)
if ((CreateIoCompletionPort((HANDLE)Accept,CompetionPort,(DWORD)PerHandleData,0)) == NULL)
{
printf("CreateIoCompletionPort failed with error%d\n",GetLastError());
return 0;
}
// 創(chuàng)建同下面的WSARecv調(diào)用相關(guān)的IO套接字信息結(jié)構(gòu)體
if ((PerIOData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) = NULL)
{
printf("GlobalAloc failed with error %d\n",GetLastError());
return 0;
}
ZeroMemory(&(PerIOData->OVerlapped),sizeof(OVERLAPPED));
PerIOData->BytesRecv = 0;
PerIOData->BytesSend = 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 0;
}
}
}
return 0;
}
工作者線程
//工作線程
DWORD WINAPI ServerWorkerThread(LPVOID ComlpetionPortID)
{
HANDLE ComplectionPort = (HANDLE) ComlpetionPortID;
DWORD BytesTransferred;
LPOVERLAPPED Overlapped;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIOData;
DWORD SendBytes,RecvBytes;
DWORD Flags;
while (TRUE)
{
if (GetQueuedCompletionStatus(ComplectionPort,&BytesTransferred,(LPDWORD)&PerHandleData,(LPOVERLAPPED*)&PerIOData,INFINITE) == 0)
{
printf("GetQueuedCompletionStatus failed with error%d\n",GetLastError());
return 0;
}
//首先檢查套接字上是否發(fā)生錯(cuò)誤,如果發(fā)生了則關(guān)閉套接字并且清除同套節(jié)字相關(guān)的SOCKET_INFORATION 結(jié)構(gòu)體
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;
}
//檢查BytesRecv域是否等于0,如果是,說明WSARecv調(diào)用剛剛完成,可以用從己完成的WSARecv調(diào)用返回的BytesTransferred值更新BytesRecv域
if (PerIOData->BytesRecv == 0)
{
PerIOData->BytesRecv = BytesTransferred;
PerIOData->BytesSend = 0;
}
else
{
PerIOData->BytesRecv +=BytesTransferred;
}
//
if (PerIOData->BytesRecv > PerIOData->BytesSend)
{
//發(fā)布另一個(gè)WSASend()請(qǐng)求,因?yàn)閃SASendi 不能確保發(fā)送了請(qǐng)的所有字節(jié),繼續(xù)WSASend調(diào)用直至發(fā)送完所有收到的字節(jié)
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() fialed with error %d\n",WSAGetLastError());
return 0;
}
}
}
else
{
PerIOData->BytesRecv = 0;
//Now that is no more bytes to send post another WSARecv() request
//現(xiàn)在己經(jīng)發(fā)送完成
Flags = 0;
ZeroMemory(&(PerIOData->OVerlapped),sizeof(OVERLAPPED));
PerIOData->DATABuf.buf = PerIOData->Buffer;
PerIOData->DATABuf.len = DATA_BUFSIZE;
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;
}
}
}
}
}