完成端口模型

最近要做一个网络方面的小东东,基于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(2, 2), &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;
}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hbyh/archive/2007/09/02/1769560.aspx

你可能感兴趣的:(socket,IO,null,application,initialization,winapi)