• 2006-11-27

    8.2.5完成端口模型 - [winsock]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://xingzhesun.blogbus.com/logs/3923197.html

    8.2.5完成端口模型
    创建完成端口对象 CreateCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, DWORD CompletionKey, DWORD NumberOfConcurrentThreads);
    CompletionKey:则指定要与某个特定套接字句柄关联在一起的“单句柄数据”
    1.工作者线程与完成端口
    StartWinsock();
    //step 1
    //create an IO completion port.

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

    //step 2;
    //Determine how many processors are on the system

    GetSystemInfo(&SystemInfo);

    //Step3
    //create worker threads based on the number of processors available on the system .For this simple case, we create one worker thread for each processor.
    for(i=0; i < SystemInfo.dwNumberOfProcessors; i++)
    {
    HANDLE ThreadHandle;
    //create a server worker thread and pass the completion port to the thread, Note:the ServerWorkerThread procedure is not defined in this listing.
    ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,0, &ThreadID);

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

    //step 4
    //create a listening socket
    Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

    InternetAddr.sin_family = AF_INET;
    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    InternetAddr.sin_port = htons(5150);
    bind(Listen, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr));

    //prepare socket for listening
    listen(Listen, 5);

    while (TRUE)
    {
    //step 5
    //Accept connections and assign to completion port.
    Accept = WSAAccept(Listen, NULL, NULL, NULL, 0);

    //step 6
    //create per-handle data information structure to associate with the socket
    PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));

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

    //step 7
    //Associate the accepted socket with the completion port.
    CreateIoCompletionPort((HANDLE)Accept,CompletionPort, (DWORD)PerHandleData,0);

    //创建和关联的动作完成后,系统会将完成端口关联的设备句柄、完成键作为一条纪录加入到这个完成端口的设备列表中。如果你有多个完成端口,就会有多个对应的设备列表。如果设备句柄被关闭,则表中自动删除该纪录。

    //step 8
    //start processing IO on the accepted socket. post oen or more WSASend() or WSARecv() calls on the socket using overlapped IO
    WSARecv(...)''
    }

    2.完成端口和重叠IO
    需要由我们的应用程序负责在以后的某个时间,通过一个OVERLAPPED结构,来接收调用的结果。
    BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, LPDWORD lpCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);

    3.单句柄数据和单IO操作数据
    typedef struct
    {
    OVERLAPPED Overlapped;
    WSABUF DataBuf;
    CHAR Buffer[DATA_BUFSIZE];
    bool OperationType;
    } PER_IO_OPERATION_DATA;

    DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
    {
    HANDLE CompletionPort = (HANDLE).CompletionPortID;
    DWORD ByteTransferred;
    LPOVERLAPPED Overlapped;
    LPPER_HANDLE_DATA PerHandleData;
    LPPER_IO_OPERATION_DATA PerIoData;
    DWORD SendBytes, RecvBytes;
    DWORD Flags;

    while (TRUE)
    {
    //Wait for IO to complete on any socket associated with the completion port
    GetQueuedCompletionStatus(CompletionPort,&ByteTransferred, (LPDWORD)&PerHandleData,(LPOVERLAPPED*)&PerIoData, INFINITE);

    //socket and clean up the per-handle data and per-io opertion data associated with the socket
    if (BytesTransferred ==0&&(PerIoData->OperationType == RECV_POSTED||PerHandleData->OperationType == SEND_POSTED))
    {
    //A zero bytesTransferred indicates that the socket has been closed by the peer, so you should close the socket.
    //Note: Per-handle data was used to reference the socket associated withe IO operation.
    closesocket(PerHandleData->Socket);

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

    //service the completed IO request, You can determine which IO request has just completed by looking at the OperationType field contained in the
    //the per-io operation data.
    if (PerIoData->OperationType==RECV_POSTED)
    {
    //Do sth with the received data in perIoData->Buffer
    }

    //post another WSASend or WSARecv operation .
    //As an example , we will post another WSARecv() IO operation

    Flags = 0;

    //Set up the per-IO operation data for the next overlapped call
    ZeroMemory(&(PerIoData->Overlapped),sizeof(OVERLAPPED));

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

    WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf),1,&RecvBytes, &Flags, &(PerIoData->Overlapped),NULL);
    }
    }

     

    如何正确地关闭IO端口-特别是同时运行一个或者多个线程,在几个不同的套接字上执行IO操作的时候,要避免的一个重要的问题是在进行重叠IO操作的同时,
    强行释放一个OVERLAPPED结构。要想避免出现这种情况,最好的办法是针对每个套接字句柄,调用closesocket,任何尚未进行的重叠IO操作都会完成。
    一旦所有套接字句柄都已经关闭,便需在完成端口上终止所有的工作者者线程的运行,PostQueuedCompletionStatus.


    收藏到:Del.icio.us