青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2007年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

最近要做一個網絡方面的小東東,基于C/S模式的。都說IOCP可以使系統達到最佳的性能,因此我就比劃了兩下,獻丑了。抄書開始。
    從本質上說,完成端口模型要求創建一個windows完成端口對象,該對象通過指定數量的線程,對重疊I/O請求進行管理,以便為已經完成的重疊I/O請求提供服務。
    首先要創建一個I/O完成端口對象,用它面向任意數量的套接字句柄,管理多個I/O請求。調用以下函數創建完成端口對象:

HANDLE CreateIoCompletionPort(
  HANDLE FileHandle
,// 同IOCP關聯在一起的套接字句柄
  HANDLE ExistingCompletionPort
,// IOCP句柄
  ULONG_PTR CompletionKey
,        // 完成健
  DWORD NumberOfConcurrentThreads // 在IOCP上,同時允許執行的線程數量
);

    該函數有兩個作用:
    (1)創建一個完成端口對象
    (2)將一個句柄同完成端口關聯到一起
    
    然后就要創建一定數量的工作者線程,以便在套接字的I/O請求投遞給完成端口后,為完成端口提供服務。寫文字描述很煩,還是看代碼吧:

// NetServer3.cpp : Defines the entry point for the console application.
//

#include 
"stdafx.h"
#include 
"NetServer3.h"

#include 
<winsock2.h>
#pragma comment(lib, 
"ws2_32.lib")

#include 
<iostream>
using namespace std;

//////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//////////////////////////////////////////////////////////////////////////

// 單句柄數據
typedef struct tagPER_HANDLE_DATA
{
    SOCKET Socket;
    SOCKADDR_STORAGE ClientAddr;
    
// 將和這個句柄關聯的其他有用信息,盡管放在這里面吧
}
PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

// 但I/O操作數據
typedef struct tagPER_IO_DATA
{
    OVERLAPPED Overlapped;
    WSABUF DataBuf;
    
char buffer[1024];
    
int BufferLen;
    
int OperationType;   // 可以作為讀寫的標志,為簡單,我忽略了
}
PER_IO_DATA, *LPPER_IO_DATA;

DWORD WINAPI ServerWorkerThread(LPVOID lpParam);

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    
int nRetCode = 0;

    
// initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    
{
        
// TODO: change error code to suit your needs
        cerr << _T("Fatal Error: MFC initialization failed"<< endl;
        nRetCode 
= 1;
    }

    
else
    
{
        
// TODO: code your application's behavior here.
        CString strHello;
        strHello.LoadString(IDS_HELLO);
        cout 
<< (LPCTSTR)strHello << endl;
    }


//////////////////////////////////////////////////////////////////////////

    HANDLE CompletionPort;
    WSADATA wsd;
    SYSTEM_INFO SystemInfo;
    SOCKADDR_IN InternetAddr;
    SOCKET Listen;

    
// 加載WinSock2.2
    WSAStartup(MAKEWORD(22), &wsd);

    
// 1.創建一個I/O完成端口
    CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                            NULL,
                                            
0,
                                            
0);

    
// 2.確定系統中有多少個處理器
    GetSystemInfo(&SystemInfo);

    
// 3.基于系統中可用的處理器數量創建工作器線程
    for (int i = 0; i < SystemInfo.dwNumberOfProcessors; ++i)
    
{
        HANDLE ThreadHandle;

        
// 創建一個服務器的工作器線程,并將完成端口傳遞到該線程
        ThreadHandle = CreateThread(NULL,
                                    
0,
                                    ServerWorkerThread,
                                    CompletionPort,
                                    
0,
                                    NULL);

        CloseHandle(ThreadHandle);
    }


    
// 4.創建一個監聽套接字,以下的套路都是固定的。
    Listen = WSASocket(AF_INET,
                       SOCK_STREAM,
                       
0,
                       NULL,
                       
0,
                       WSA_FLAG_OVERLAPPED);

    InternetAddr.sin_family 
= PF_INET;
    InternetAddr.sin_port 
= htons(5000);
    InternetAddr.sin_addr.s_addr 
= htonl(INADDR_ANY);

    bind(Listen, (SOCKADDR
*)&InternetAddr, sizeof(InternetAddr));

    listen(Listen, 
5);

    BOOL b 
= TRUE;

    
while (b)
    
{
        PER_HANDLE_DATA 
* PerHandleData = NULL;
        SOCKADDR_IN saRemote;
        SOCKET Accept;
        
int RemoteLen;

        
// 5.接收連接,并分配完成端口,這兒可以用AcceptEx來代替,以創
         // 建可伸縮的Winsock應用程序。

        RemoteLen = sizeof(saRemote);
        Accept 
= accept(Listen, (SOCKADDR*)&saRemote, &RemoteLen);

        
// 6.創建用來和套接字關聯的單句柄數據信息結構
        PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, 
                                                       
sizeof(PER_HANDLE_DATA));

        cout 
<< "Socket number " << Accept << " connected" << endl;

        PerHandleData
->Socket = Accept;
        memcpy(
&PerHandleData->ClientAddr, &saRemote, RemoteLen);

        
// 7.將接受套接字和完成端口關聯起來
        CreateIoCompletionPort((HANDLE)Accept,
                               CompletionPort,
                               (DWORD)PerHandleData,
                               
0);

        
// 開始在接受套接字上處理I/O
        
// 使用重疊I/O機制,在新建的套接字上投遞一個或多個異步
         // WSARecv 或 WSASend請求。這些I/O請求完成后,工作者線程
         // 會為I/O請求提供服務,之后就可以坐享其成了

        static int const DATA_BUFSIZE = 4096; //

        DWORD RecvBytes 
= 0;
        DWORD Flags 
= 0;

        
// 單I/O操作數據
        LPPER_IO_DATA PerIoData = NULL;
        PerIoData 
= (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
        ZeroMemory(
&(PerIoData->Overlapped), sizeof(OVERLAPPED));        

        PerIoData
->DataBuf.len = 1024;
        PerIoData
->DataBuf.buf = PerIoData->buffer;
        PerIoData
->OperationType = 0// read
        WSARecv(PerHandleData->Socket,
                
&(PerIoData->DataBuf),
                
1,
                
&RecvBytes,
                
&Flags,
                
&(PerIoData->Overlapped),
                NULL);
    }


//////////////////////////////////////////////////////////////////////////

    
return nRetCode;
}


//////////////////////////////////////////////////////////////////////////

DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{
    HANDLE CompletionPort 
= (HANDLE)lpParam;
    DWORD BytesTransferred;
    LPOVERLAPPED lpOverlapped;
    LPPER_HANDLE_DATA PerHandleData 
= NULL;
    LPPER_IO_DATA PerIoData 
= NULL;
    DWORD SendBytes;
    DWORD RecvBytes;
    DWORD Flags;
    BOOL bRet 
= FALSE;

    
while (TRUE)
    
{
        bRet 
= GetQueuedCompletionStatus(CompletionPort,
                                         
&BytesTransferred,
                                         (PULONG_PTR)
  
                                           &
PerHandleData,
                                         (LPOVERLAPPED
*)
                                           &lpOverlapped,
                                         INFINITE);

        
// 檢查成功的返回,這兒要注意使用這個宏CONTAINING_RECORD
        PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(lpOverlapped, 
                                                     PER_IO_DATA, 
                                                     Overlapped);

        
// 先檢查一下,看看是否在套接字上已有錯誤發生
        if (0 == BytesTransferred) 
        
{
            closesocket(PerHandleData
->Socket);
            GlobalFree(PerHandleData);
            GlobalFree(PerIoData);

            
continue;
        }


        
// 數據處理
        
// 成功了!!!這兒就收到了來自客戶端的數據
        cout << PerIoData->DataBuf.buf << endl;

        Flags 
= 0;

        
// 為下一個重疊調用建立單I/O操作數據
        ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

        PerIoData
->DataBuf.len = 1024;
        PerIoData
->DataBuf.buf = PerIoData->buffer;
        PerIoData
->OperationType = 0// read
        WSARecv(PerHandleData->Socket,
                
&(PerIoData->DataBuf),
                
1,
                
&RecvBytes,
                
&Flags,
                
&(PerIoData->Overlapped),
                NULL);
    }


    
return 0;
}


//////////////////////////////////////////////////////////////////////////




   當然為了測試,各種異常處理都沒有寫,大家不要學我哦。
posted on 2007-08-17 13:57 聶文龍 閱讀(6687) 評論(5)  編輯 收藏 引用 所屬分類: net work

FeedBack:
# re: 完成端口模型代碼 2007-08-17 14:12 聶文龍
一起來分析一下最簡單的完成端口代碼

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


#include "Socket5.h"

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


//初始化WinSock2
if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
printf("WSAStartup failed with error %u\n", Ret);
return;
}
//創建完成端口對象
if((CompletionPort=CreateIoCompletionPort(INVALID_HANDLE_value,NULL,0,0))==NULL)
{
printf( "CreateIoCompletionPort failed with error: %u\n", GetLastError());
return;
}
//判斷CPU數量
GetSystemInfo(&SystemInfo);
printf("您的機器有%d個CPU\n",SystemInfo.dwNumberOfProcessors);
//為每個CPU建立一個工作線程
for(i=0;i<SystemInfo.dwNumberOfProcessors;i++)
{
HANDLE ThreadHandle;
if((ThreadHandle=CreateThread(NULL,0,ServerWorkerThread,CompletionPort,0,&ThreadID)) == NULL)
{
printf("CreateThread() failed with error %u\n", GetLastError());
return;
}
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(SOCKS_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;
}

printf("Server started at port : %d\n",SOCKS_PORT);

//接受每一個連接并將其關聯到完成端口上
while(TRUE)
{
//接受連接
if((Accept=WSAAccept(Listen,NULL,NULL,NULL,0))==SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return;
}
printf(" Client Socket number %d connected\n", Accept);

//創建包含接受的Socket信息的單句柄數據結構體
if((PerHandleData=(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA)))==NULL)
{
printf("GlobalAlloc() failed with error %u\n", GetLastError());
return;
}

//將Accept關聯到完成端口

if(CreateIoCompletionPort((HANDLE)Accept,CompletionPort,(DWORD)PerHandleData,0)==NULL)
{
printf("CreateIoCompletionPort failed with error %u\n", GetLastError());
return;
}
//創建單I/O操作數據
if((PerIoData=(LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA)))==NULL)
{
printf("GlobalAlloc() failed with error %u\n", GetLastError());
return;
}

PerHandleData->self=Accept;
PerHandleData->isClient=TRUE;
PerHandleData->SelfPerHandleData=PerHandleData;
PerHandleData->SelfPerIoData=PerIoData;

//接收客戶端的版本信息
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->Operation=OP_READ;
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;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD Flags;
DWORD RecvBytes;

while(TRUE)
{
//使用GetQueuedCompletionStatus查詢
if(GetQueuedCompletionStatus(CompletionPort,&BytesTransferred,(LPDWORD)&PerHandleData,(LPOVERLAPPED *)&PerIoData,INFINITE)==0)
{
int iError=GetLastError();
printf("GetQueuedCompletionStatus failed with error %u\n", iError);
if(iError==64)
{
continue;
}
else
{
printf("return 0\n");
return 0;
}
}
if(BytesTransferred==0)
{
//遠端斷開連接,關閉本機SOCKET
printf("Closing ");
if(PerHandleData->isClient==TRUE)
printf("Client");
else
printf("Dest");
printf(" socket %u\n", PerHandleData->self);

closesocket(PerHandleData->self);

//在此關閉和此SOCKET相關聯的SOCKET
printf("Closing ");
if(PerHandleData->isClient==TRUE)
printf("Dest");
else
printf("Client");
printf(" socket %u\n", PerHandleData->other);
closesocket(PerHandleData->other);

GlobalFree(PerHandleData->OtherPerHandleData->SelfPerIoData);
GlobalFree(PerHandleData->OtherPerHandleData);

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

switch(PerIoData->Operation)
{
case OP_READ:

WSARecv(PerHandleData->self,&PerHandleData->SelfPerIoData->DataBuf ,1,&RecvBytes,&Flags,&(PerHandleData->SelfPerIoData->Overlapped),NULL);
printf("%s\n",PerHandleData->SelfPerIoData->DataBuf.buf); //顯示收到的數據
break;

case OP_WRITE:
printf("write...");
break;

case OP_ACCEPT:
printf("accept...");
break;
}//end of switch

}//end of while
return 0;
}//end of function

為什么我客戶連接好以后 只能顯示第一次發送的數據。。。以后發送數據就顯示不出來了????

  回復  更多評論
  
# re: 完成端口模型代碼 2007-08-17 14:13 聶文龍
//采用完成端口的代理服務器原型代碼
http://www.vckbase.com/code/listcode.asp?mclsid=9&sclsid=901

---------------------------------------------------------------

http://www.vctop.com/View.Asp?ID=484&CateID=1
---------------------------------------------------------------

服務器程序:
http://www.cnxbb.com/bcb/xbb_server_iocp.rar

模擬多客戶端程序
http://www.cnxbb.com/bcb/EchoClient.rar
---------------------------------------------------------------

// Compile:
//
// cl -o callback callback.cpp ws2_32.lib
//
// Command Line Options:
//
// callback.exe
//
// Note: There are no command line options for this sample.

//
// 阻塞模式+重疊模型+完成例程機制,王天平,2003-06-20
//

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

#define PORT 5150
#define DATA_BUFSIZE 8192

//套接字信息數組單元結構
typedef struct _SOCKET_INFORMATION {
OVERLAPPED Overlapped;//重疊結構
SOCKET Socket;//套接字
CHAR Buffer[DATA_BUFSIZE];//WSARecv/WSASend 數據緩沖區指針
WSABUF DataBuf;//WSARecv/WSASend 數據緩沖區
DWORD BytesSEND;//發送字節數
DWORD BytesRECV;//接收字節數
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred,
LPWSAOVERLAPPED Overlapped, DWORD InFlags);

DWORD WINAPI WorkerThread(LPVOID lpParameter);

SOCKET AcceptSocket;

void main(void)
{
WSADATA wsaData;
SOCKET ListenSocket;
SOCKADDR_IN InternetAddr;
INT Ret;
HANDLE ThreadHandle;
DWORD ThreadId;
WSAEVENT AcceptEvent;

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

if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("Failed to get a socket %d\n", WSAGetLastError());
return;
}

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

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

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

if ((AcceptEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
{
printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
return;
}

// Create a worker thread to service completed I/O requests.

if ((ThreadHandle = CreateThread(NULL, 0, WorkerThread, (LPVOID) AcceptEvent, 0, &ThreadId)) == NULL)
{
printf("CreateThread failed with error %d\n", GetLastError());
return;
}

while(TRUE)
{
AcceptSocket = accept(ListenSocket, NULL, NULL);

if (WSASetEvent(AcceptEvent) == FALSE)
{
printf("WSASetEvent failed with error %d\n", WSAGetLastError());
return;
}
}
}

DWORD WINAPI WorkerThread(LPVOID lpParameter)
{
DWORD Flags;
LPSOCKET_INFORMATION SocketInfo;
WSAEVENT EventArray[1];
DWORD Index;
DWORD RecvBytes;

// Save the accept event in the event array.

EventArray[0] = (WSAEVENT) lpParameter;

while(TRUE)
{
// Wait for accept() to signal an event and also process WorkerRoutine() returns.

while(TRUE)
{
Index = WSAWaitForMultipleEvents(1, EventArray, FALSE, WSA_INFINITE, TRUE);

if (Index == WSA_WAIT_FAILED)
{
printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());
return FALSE;
}

if (Index != WAIT_IO_COMPLETION)
{
// An accept() call event is ready - break the wait loop
break;
}
}

WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

// Create a socket information structure to associate with the accepted socket.

if ((SocketInfo = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,
sizeof(SOCKET_INFORMATION))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return FALSE;
}

// Fill in the details of our accepted socket.

SocketInfo->Socket = AcceptSocket;
ZeroMemory(&(SocketInfo->Overlapped), sizeof(WSAOVERLAPPED));
SocketInfo->BytesSEND = 0;
SocketInfo->BytesRECV = 0;
SocketInfo->DataBuf.len = DATA_BUFSIZE;
SocketInfo->DataBuf.buf = SocketInfo->Buffer;

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

printf("Socket %d connected\n", AcceptSocket);
}

return TRUE;
}

void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred,
LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
DWORD SendBytes, RecvBytes;
DWORD Flags;

// Reference the WSAOVERLAPPED structure as a SOCKET_INFORMATION structure
LPSOCKET_INFORMATION SI = (LPSOCKET_INFORMATION) Overlapped;

if (Error != 0)
{
printf("I/O operation failed with error %d\n", Error);
}

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

if (Error != 0 ¦ ¦ BytesTransferred == 0)
{
closesocket(SI->Socket);
GlobalFree(SI);
return;
}

// Check to see if the BytesRECV field equals zero. If this is so, then
// this means a WSARecv call just completed so update the BytesRECV field
// with the BytesTransferred value from the completed WSARecv() call.

if (SI->BytesRECV == 0)
{
SI->BytesRECV = BytesTransferred;
SI->BytesSEND = 0;
}
else
{
SI->BytesSEND += BytesTransferred;
}

if (SI->BytesRECV > SI->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(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

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

if (WSASend(SI->Socket, &(SI->DataBuf), 1, &SendBytes, 0,
&(SI->Overlapped), WorkerRoutine) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
printf("WSASend() failed with error %d\n", WSAGetLastError());
return;
}
}
}
else
{
SI->BytesRECV = 0;

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

Flags = 0;
ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

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

if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &RecvBytes, &Flags,
&(SI->Overlapped), WorkerRoutine) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING )
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
}
}

  回復  更多評論
  
# re: 完成端口模型代碼 2007-09-24 22:02 聶文龍
眾所皆知,完成端口是在WINDOWS平臺下效率最高,擴展性最好的IO模型,特別針對于WINSOCK的海量連接時,更能顯示出其威力。其實建立一個完成端口的服務器也很簡單,只要注意幾個函數,了解一下關鍵的步驟也就行了。

這是篇完成端口入門級的文章,分為以下幾步來說明完成端口:

函數
常見問題以及解答
步驟
例程
1、函數:

我們在完成端口模型下會使用到的最重要的兩個函數是:
CreateIoCompletionPort、GetQueuedCompletionStatus

CreateIoCompletionPort 的作用是創建一個完成端口和把一個IO句柄和完成端口關聯起來:

// 創建完成端口
HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

// 把一個IO句柄和完成端口關聯起來,這里的句柄是一個socket 句柄
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

其中第一個參數是句柄,可以是文件句柄、SOCKET句柄。
第二個就是我們上面創建出來的完成端口,這里就把兩個東西關聯在一起了。
第三個參數很關鍵,叫做PerHandleData,就是對應于每個句柄的數據塊。我們可以使用這個參數在后面取到與這個SOCKET對應的數據。
最后一個參數給0,意思就是根據CPU的個數,允許盡可能多的線程并發執行。

GetQueuedCompletionStatus 的作用就是取得完成端口的結果:

// 從完成端口中取得結果
GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)

第一個參數是完成端口
第二個參數是表明這次的操作傳遞了多少個字節的數據
第三個參數是OUT類型的參數,就是前面CreateIoCompletionPort傳進去的單句柄數據,這里就是前面的SOCKET句柄以及與之相對應的數據,這里操作系統給我們返回,讓我們不用自己去做列表查詢等操作了。
第四個參數就是進行IO操作的結果,是我們在投遞 WSARecv / WSASend 等操作時傳遞進去的,這里操作系統做好準備后,給我們返回了。非常省事!!

個人感覺完成端口就是操作系統為我們包裝了很多重疊IO的不爽的地方,讓我們可以更方便的去使用,下篇我將會嘗試去講述完成端口的原理。

2、常見問題和解答

a、什么是單句柄數據(PerHandle)和單IO數據(PerIO)

單句柄數據就是和句柄對應的數據,像socket句柄,文件句柄這種東西。

單IO數據,就是對應于每次的IO操作的數據。例如每次的WSARecv/WSASend等等

其實我覺得PER是每次的意思,翻譯成每個句柄數據和每次IO數據還比較清晰一點。

在完成端口中,單句柄數據直接通過GetQueuedCompletionStatus 返回,省去了我們自己做容器去管理。單IO數據也容許我們自己擴展OVERLAPPED結構,所以,在這里所有與應用邏輯有關的東西都可以在此擴展。

b、如何判斷客戶端的斷開

我們要處理幾種情況

1) 如果客戶端調用了closesocket,我們就可以這樣判斷他的斷開:

if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, 。。。)
{
}
if(BytesTransferred == 0)
{
// 客戶端斷開,釋放資源
}

2) 如果是客戶端直接退出,那就會出現64錯誤,指定的網絡名不可再用。這種情況我們也要處理的:

if(0 == GetQueuedCompletionStatus(。。。))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
// 客戶端斷開,釋放資源
}
}

3、步驟

編寫完成端口服務程序,無非就是以下幾個步驟:

1、創建一個完成端口
2、根據CPU個數創建工作者線程,把完成端口傳進去線程里
3、創建偵聽SOCKET,把SOCKET和完成端口關聯起來
4、創建PerIOData,向連接進來的SOCKET投遞WSARecv操作

5、線程里所做的事情:
a、GetQueuedCompletionStatus,在退出的時候就可以使用PostQueudCompletionStatus使線程退出
b、取得數據并處理

4、例程

下面是服務端的例程,可以使用《WinSocket模型的探討——Overlapped模型(一)》中的客戶端程序來測試次服務端。稍微研究一下,也就會對完成端口模型有個大概的了解了。

/*

完成端口服務器

接收到客戶端的信息,直接顯示出來

*/

#include "winerror.h"
#include "Winsock2.h"
#pragma comment(lib, "ws2_32")

#include "windows.h"


#include <iostream>
using namespace std;


/// 宏定義
#define PORT 5050
#define DATA_BUFSIZE 8192

#define OutErr(a) cout << (a) << endl \
<< "出錯代碼:" << WSAGetLastError() << endl \
<< "出錯文件:" << __FILE__ << endl \
<< "出錯行數:" << __LINE__ << endl \

#define OutMsg(a) cout << (a) << endl;


/// 全局函數定義


///////////////////////////////////////////////////////////////////////
//
// 函數名 : InitWinsock
// 功能描述 : 初始化WINSOCK
// 返回值 : void
//
///////////////////////////////////////////////////////////////////////
void InitWinsock()
{
// 初始化WINSOCK
WSADATA wsd;
if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
OutErr("WSAStartup()");
}
}

///////////////////////////////////////////////////////////////////////
//
// 函數名 : BindServerOverlapped
// 功能描述 : 綁定端口,并返回一個 Overlapped 的Listen Socket
// 參數 : int nPort
// 返回值 : SOCKET
//
///////////////////////////////////////////////////////////////////////
SOCKET BindServerOverlapped(int nPort)
{
// 創建socket
SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

// 綁定端口
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(nPort);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
{
OutErr("bind Failed!");
return NULL;
}

// 設置監聽隊列為200
if(listen(sServer, 200) != 0)
{
OutErr("listen Failed!");
return NULL;
}
return sServer;
}


/// 結構體定義
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


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


DWORD WINAPI ProcessIO(LPVOID lpParam)
{
HANDLE CompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;

while(true)
{

if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
cout << "closing socket" << PerHandleData->Socket << endl;

closesocket(PerHandleData->Socket);

delete PerIoData;
delete PerHandleData;
continue;
}
else
{
OutErr("GetQueuedCompletionStatus failed!");
}
return 0;
}

// 說明客戶端已經退出
if(BytesTransferred == 0)
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
}

// 取得數據并處理
cout << PerHandleData->Socket << "發送過來的消息:" << PerIoData->Buffer << endl;

// 繼續向 socket 投遞WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}

return 0;
}

void main()
{
InitWinsock();

HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

// 根據系統的CPU來創建工作者線程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);

for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
if(hProcessIO)
{
CloseHandle(hProcessIO);
}
}

// 創建偵聽SOCKET
SOCKET sListen = BindServerOverlapped(PORT);


SOCKET sClient;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(true)
{
// 等待客戶端接入
//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
sClient = accept(sListen, 0, 0);

cout << "Socket " << sClient << "連接進來" << endl;

PerHandleData = new PER_HANDLE_DATA();
PerHandleData->Socket = sClient;

// 將接入的客戶端和完成端口聯系起來
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

// 建立一個Overlapped,并使用這個Overlapped結構對socket投遞操作
PerIoData = new PER_IO_OPERATION_DATA();

ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;

// 投遞一個WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}

DWORD dwByteTrans;
PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);
closesocket(sListen);
}

好了,本篇文章就到此為止,
  回復  更多評論
  
# re: 完成端口模型代碼 2009-04-02 07:05 xxq57@163.com
個人的幾點理解:這里共享下:
1.疑問:if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)。語句中參數PerIoData怎么能代用啊?這里參數類型不是(LPOVERLAPPED *) 嗎
解惑:其實這里只是一個指針。
在ACCEPT后,有個WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,&(PerIoData->Overlapped), NULL)異步接收。注意是異步接收,所以函數執行后,先返回,但他會接收一次數據,放在PerIoData->DataBuf。其中參數&(PerIoData->Overlapped)地址值就是后面(LPOVERLAPPED *) 要傳出的地址值,二者剛好重合,其實就是指向同一內存地址。所以GetQueuedCompletionStatus()后PerIoData,和WSARecv()的PerIoData是同一個地址。所以PerIoData->DataBuf就有了。
  回復  更多評論
  
# re: 完成端口模型代碼 2009-04-02 07:09 xxq57@163.com
指針是個很巧妙的東西,上解答,如果PerIoData->Overlapped的Overlapped成員不是放在第一個位置,那結果就不是那么回事了。不信的讀者可以試試看。  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              国产色爱av资源综合区| 黄色精品一二区| 国产日韩一区欧美| 快播亚洲色图| 久久久久久亚洲精品杨幂换脸| 久久九九国产精品怡红院| 国产精品大全| 在线视频一区二区| 亚洲美女av网站| 欧美日韩免费区域视频在线观看| 99视频超级精品| 亚洲一区二区免费看| 国产农村妇女毛片精品久久莱园子| 亚洲一区二区在线观看视频| 午夜久久久久久久久久一区二区| 国产日韩欧美在线看| 欧美一区二区三区电影在线观看| 亚洲婷婷在线| 伊人久久大香线蕉av超碰演员| 亚洲一级影院| 欧美亚洲午夜视频在线观看| 韩国三级电影一区二区| 亚洲美女av电影| 国产亚洲精品久久飘花| 亚洲三级视频| 一区二区三区在线免费播放| 国产在线欧美日韩| 亚洲欧洲日产国产网站| 一区在线观看| 亚洲欧美在线一区二区| 亚洲美女av在线播放| 久久久av水蜜桃| 久久影院午夜论| 国产亚洲欧美色| 久久黄金**| 久久久久久夜| 国产情人综合久久777777| 一区二区三区国产盗摄| 亚洲私人影院| 国产精品久在线观看| 亚洲图片欧美日产| 午夜国产一区| 国产一区视频网站| 久久精品亚洲热| 欧美电影免费观看高清| 在线视频精品一| 免费不卡在线观看av| 欧美黑人国产人伦爽爽爽| 亚洲精品1区| 欧美大片18| 久久综合色影院| 欧美影片第一页| 99爱精品视频| 一区二区av| 亚洲欧美国产高清va在线播| 亚洲日韩欧美视频| 国产伦一区二区三区色一情| 欧美午夜精品一区二区三区| 欧美国产日本韩| 欧美日韩一区二区免费在线观看| 欧美日韩精品在线观看| 欧美午夜精品理论片a级按摩| 欧美揉bbbbb揉bbbbb| 欧美激情综合五月色丁香小说| 国产精品视频免费| 日韩亚洲综合在线| 国内精品久久久久影院优| 国产美女精品一区二区三区| 欧美成人资源网| 久久精品国产999大香线蕉| 99国产精品久久久久久久久久| 久久精品国产亚洲一区二区三区 | 激情久久影院| 国产精品爽黄69| 国产精品亚洲综合| 国产美女精品免费电影| 国产日韩精品电影| 国际精品欧美精品| 亚洲黄色有码视频| 夜久久久久久| 性欧美办公室18xxxxhd| 欧美一区二区三区喷汁尤物| 久久电影一区| 欧美成人精品一区| 亚洲欧洲日本在线| 一区二区av| 欧美综合第一页| 欧美激情一区二区三区全黄| 国产精品丝袜xxxxxxx| 欧美人妖在线观看| 久久精品视频网| 欧美国产免费| 在线看欧美视频| 欧美日韩免费精品| 亚洲免费婷婷| 欧美激情麻豆| 一区二区三区偷拍| 韩国久久久久| 国产精品久久久对白| 欧美一区二区三区另类| 一区二区欧美日韩视频| 中日韩男男gay无套| 国产精品日韩欧美一区二区| 久久成人18免费观看| 久久精品一区蜜桃臀影院 | 亚洲视频中文字幕| 久久久久国色av免费观看性色| 欧美人成网站| 亚洲日本va午夜在线影院| 午夜影院日韩| 亚洲精品在线二区| 暖暖成人免费视频| 尤物精品在线| 久久综合给合久久狠狠狠97色69| 日韩一级二级三级| 欧美日韩伦理在线| 99视频精品免费观看| 亚洲福利视频网站| 久久字幕精品一区| 精品不卡一区| 欧美不卡激情三级在线观看| 亚洲欧美视频一区| 国产亚洲成av人片在线观看桃| 亚洲一区二区三区四区五区午夜 | 老司机免费视频久久| 国内精品久久国产| 欧美**人妖| 欧美激情第9页| 亚洲一区二区三区三| 午夜伦理片一区| 在线观看一区| 99在线精品观看| 国内久久精品| 亚洲韩日在线| 国产欧美亚洲日本| 欧美成人激情视频| 欧美激情综合五月色丁香小说| 亚洲制服av| 久久午夜视频| 亚洲一区二区三区影院| 久久精品国产久精国产爱| 久久国产日韩| 夜夜嗨av色综合久久久综合网 | 国产伦精品一区二区三区视频孕妇 | 欧美日韩精品综合| 欧美一区二区三区在线| 久久人人爽人人爽| 亚洲在线一区二区| 欧美全黄视频| 亚洲第一福利在线观看| 国产日韩1区| 亚洲一区综合| 午夜精品久久99蜜桃的功能介绍| 久久天天躁夜夜躁狠狠躁2022| 亚洲一区二区成人在线观看| 欧美国产一区二区三区激情无套| 久久久噜噜噜| 国产专区精品视频| 午夜精品999| 久久国产精品99国产| 国产精品一区二区三区乱码 | 亚洲看片一区| 日韩视频中文字幕| 欧美成人精品在线| 欧美成ee人免费视频| 亚洲国产99精品国自产| 久久美女性网| 欧美激情a∨在线视频播放| 亚洲国产精品va在线看黑人动漫| 久久九九国产精品| 亚洲人精品午夜在线观看| 亚洲少妇一区| 国产一区二区三区直播精品电影 | 韩国免费一区| 欧美精品国产精品日韩精品| 日韩视频永久免费| 国产亚洲欧美在线| 久久久xxx| 亚洲精品在线三区| 久久成人免费| 一本到12不卡视频在线dvd| 国产色产综合色产在线视频| 玖玖视频精品| 亚洲欧美成人精品| 亚洲欧洲视频| 麻豆9191精品国产| 欧美在线视频a| 一本色道婷婷久久欧美| 激情成人av| 国产美女精品免费电影| 欧美日韩国产成人精品| 久久九九热re6这里有精品| 亚洲免费高清视频| 亚洲国产精品第一区二区| 久久精品论坛| 久久精品1区| 欧美制服丝袜| 亚洲日本在线观看| 国产精品一区二区视频 | 久久综合伊人77777尤物|