Socket I/O模型之完成端口(completion port)

“完成端口”模型是迄今为止最为复杂的一种I/O模型。然而,假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!但不幸的是,该模型只适用于Windows NT和Windows 2000操作系统。因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。要记住的一个基本准则是,假如要为Windows NT或Windows 2000开发高性能的服务器应用,同时希望为大量套接字I/O请求提供服务(Web服务器便是这方面的典型例子),

那么I/O完成端口模型便是最佳选择!使用完成端口模型的服务器端代码:// write by larry
// 2009-8-20
// This is a server using overlapped IO(completion routine).
#include "stdafx.h"
#include <WINSOCK2.H>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT  5150
#define MSGSIZE  1024
typedef struct
{
    WSAOVERLAPPED overlap;
    WSABUF        Buffer;
    char          szMessage[MSGSIZE];
    DWORD         NumberOfBytesRecvd;
    DWORD         Flags;
    SOCKET        sClient;
} PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
int                     g_iTotalConn = 0;
SOCKET                  g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT                g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
LPPER_IO_OPERATION_DATA g_pPerIoDataArr[MAXIMUM_WAIT_OBJECTS];
DWORD WINAPI WorkerThread(LPVOID lpParam);
void CALLBACK CompletionRoutine(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
SOCKET g_sNewClientConnection;
BOOL g_bNewConnectionArrived = FALSE;

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET sListen;
    SOCKADDR_IN local, client;
    DWORD dwThreadId;
    int iAddrSize = sizeof(SOCKADDR_IN);
    // Initialize windows socket library
    WSAStartup(0x0202, &wsaData);
    // Create listening socket
    sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    // Bind
    local.sin_family = AF_INET;
    local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    local.sin_port = htons(PORT);
    bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));
    // Listen
    listen(sListen, 3);
    // Create worker thread
    CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
    while (TRUE)
    {
        // Accept a connection
        g_sNewClientConnection = accept(sListen, (sockaddr*)&client, &iAddrSize);
        g_bNewConnectionArrived = TRUE;
        printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));        
    }
    return 0;
}
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
    LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
    while (TRUE)
    {
        if (g_bNewConnectionArrived)
        {
            // Launch an asynchronous operation for new arrived connection
            lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
                GetProcessHeap(),
                HEAP_ZERO_MEMORY,
                sizeof(PER_IO_OPERATION_DATA));
            lpPerIOData->Buffer.len = MSGSIZE;
            lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
            lpPerIOData->sClient = g_sNewClientConnection;
            WSARecv(lpPerIOData->sClient,
                &lpPerIOData->Buffer,
                1,
                &lpPerIOData->NumberOfBytesRecvd,
                &lpPerIOData->Flags,
                &lpPerIOData->overlap,
                CompletionRoutine);
            g_bNewConnectionArrived = FALSE;
        }
        SleepEx(1000, TRUE);
    }
    return 0;
}
void CALLBACK CompletionRoutine(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    LPPER_IO_OPERATION_DATA lpPerIOData = (LPPER_IO_OPERATION_DATA)lpOverlapped;
    if (dwError != 0 || cbTransferred == 0)
    {
        // Connection was closed by client
        closesocket(lpPerIOData->sClient);
        HeapFree(GetProcessHeap(), 0, lpPerIOData);
    }
    else
    {
        lpPerIOData->szMessage[cbTransferred] = '\0';
        send(lpPerIOData->sClient, lpPerIOData->szMessage, cbTransferred, 0);
        // Launch another asynchronous operation
        memset(&lpPerIOData->overlap, 0, sizeof(WSAOVERLAPPED));
        lpPerIOData->Buffer.len = MSGSIZE;
        lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
        WSARecv(lpPerIOData->sClient,
            &lpPerIOData->Buffer,
            1,
            &lpPerIOData->NumberOfBytesRecvd,
            &lpPerIOData->Flags,
            &lpPerIOData->overlap,
            CompletionRoutine);
    }
}

 服务器端得主要流程:1.创建完成端口对象2.创建工作者线程(这里工作者线程的数量是按照CPU的个数来决定的,这样可以达到最佳性能)3.创建监听套接字,绑定,监听,然后程序进入循环4.在循环中,我做了以下几件事情:(1).接受一个客户端连接(2).将该客户端套接字与完成端口绑定到一起(还是调用CreateIoCompletionPort,但这次的作用不同),注意,按道理来讲,此时传递给CreateIoCompletionPort的第三个参数应该是一个完成键,一般来讲,程序都是传递一个单句柄数据结构的地址,该单句柄数据包含了和该客户端连接有关的信息,由于我们只关心套接字句柄,所以直接将套接字句柄作为完成键传递;(3).触发一个WSARecv异步调用,这次又用到了“尾随数据”,使接收数据所用的缓冲区紧跟在WSAOVERLAPPED对象之后,此外,还有操作类型等重要信息。在工作者线程的循环中,我们1.调用GetQueuedCompletionStatus取得本次I/O的相关信息(例如套接字句柄、传送的字节数、单I/O数据结构的地址等等)2.通过单I/O数据结构找到接收数据缓冲区,然后将数据原封不动的发送到客户端3.再次触发一个WSARecv异步操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值