进行重叠IO时,如何处理多于64个并发连接?前面有个贴子谈论这个问题,可惜打不开了. :(

解决方案 »

  1.   

    为什么要用完成端口?
    我看了一下完成端口的介绍,它只在多CPU的情况下可以提升程序的性能,
    而且使用比较复杂.
      

  2.   

    不是吧!在单cpu的机器上同样高效,而且没有64这个限制
      

  3.   

    /*
     * EchoCli.c
     *
     * Sample code for "Multithreading Applications in Win32"
     * This is from Chapter 6.
     *
     * This is a command line client to drive the ECHO server.
     * Run the server in one Commmand Prompt Window,
     * then run this program in one or more other windows.
     * EchoCli will wait for you to type in some text when
     * it starts up. Each line of text will be sent to the
     * echo server on TCP port 5554.
     */#include <windows.h>
    #include <tchar.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <winsock.h>
    /* Function Prototypes */
    void FatalError(char *s);
    int writen(SOCKET sock, char *ptr, int nBytes);
    int readline(SOCKET sock, char *ptr, int maxlen);
    void DoClientLoop(FILE *fp, SOCKET sock);/* Constants */
    #define MAXLINE 512
    #define SERVER_TCP_PORT 5554
    #define SERVER_ADDRESS "127.0.0.1"
    /*
     * Error handler
     */
    void FatalError(char *s)
    {
        fprintf(stdout, "%s\n", s);
        exit(EXIT_FAILURE);
    }/*
     * Write bytes to the port with proper block size handling.
     */
    int writen(SOCKET sock, char *ptr, int nBytes)
    {
        int nleft;
        int nwritten;    nleft = nBytes;
        while (nleft > 0)
        {
           nwritten = send(sock,
                       ptr,
                       nBytes,
                       0
              );       if (nwritten == SOCKET_ERROR)
           {
              fprintf(stdout, "Send Failed\n");
              exit(1);
           }        nleft -= nwritten;
            ptr += nwritten;
        }    return nBytes - nleft;
    }/*
     * Read a line of text of the port. This version
     * is very inefficient, but it's simple.
     */
    int readline(SOCKET sock, char *ptr, int maxlen)
    {
        int n;
        int rc;
        char c;    for (n=1; n<maxlen; n++)
        {
            if ( ( rc= recv(sock, &c, 1, 0)) == 1)
            {
                *ptr++ = c;
                if (c=='\n')
                    break;
            }
            else if (rc == 0)
            {
                if (n == 1)
                    return 0;
                else
                    break;
            }
            else
                return -1;  /* Error */
        }    *ptr = '\0';
        return n;
    }int main(int argc, char *argv[])
    {
        WSADATA WsaData;
        SOCKET sock;
        struct sockaddr_in  serv_addr;
        int err;    puts("EchoCli - client for echo server sample program\n");
        puts("Type a line of text followed by Return.");
        puts("Exit this program by typing Ctrl+Z followed by Return.");    err = WSAStartup(0x0101, &WsaData);
        if (err == SOCKET_ERROR)
            FatalError("WSAStartup Failed");    /*
         * Bind our local address
         */
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family    = AF_INET;
        // Use the local host
        serv_addr.sin_addr.s_addr   = inet_addr(SERVER_ADDRESS);
        serv_addr.sin_port          = htons(SERVER_TCP_PORT);    /*
         * Open a TCP socket (an Internet stream socket)
         */    sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
            FatalError("socket() failed -- do you have TCP/IP networking installed?");    if (connect(sock, (struct sockaddr *) &serv_addr,
                    sizeof(serv_addr)) < 0)
            FatalError("connect() failed -- is the server running?");    DoClientLoop(stdin, sock);    closesocket(sock);    return EXIT_SUCCESS;
    }/*
     * As long as there is input from "fp", copy it to
     * the server, read what the server gives back,
     * and print it.
     */
    void DoClientLoop(FILE *fp, SOCKET sock)
    {
        int n;
        char sendline[MAXLINE];
        char recvline[MAXLINE+1];    while (fgets(sendline, MAXLINE, fp) != NULL)
        {
            n = strlen(sendline);
            if (writen(sock, sendline, n) != n)
                FatalError("DoClientLoop: writen() error");        n = readline(sock, recvline, MAXLINE);
            if (n < 0)
                FatalError("DoClientLoop: readline() error");
            recvline[n] = '\0';
            fputs(recvline, stdout);
        }    if (ferror(fp))
            FatalError("DoClientLoop: error reading file");
    }
      

  4.   

    /*
     * EchoSrv.c
     *
     * Sample code for Multithreading Applications in Win32
     * This is from Chapter 6, Listing 6-4
     *
     * Demonstrates how to use I/O completion ports
     * with TCP on the Internet.  This sample
     * server can only be run on Windows NT,
     * version 3.51 or later.  The client (EchoCli)
     * can be run on Windows 95.
     */#define WIN32_LEAN_AND_MEAN
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <tchar.h>
    #include <string.h>
    #include <winsock.h>
    #include <io.h>
    #include "MtVerify.h"// Pick a port number that seems to be away from all others
    #define SERV_TCP_PORT 5554
    #define MAXLINE 512//
    // Structure definition
    //// The context key keeps track of how the I/O
    // is progressing for each individual file handle.
    struct ContextKey
    {
        SOCKET  sock;
        // Input
        char        InBuffer[4];
        OVERLAPPED  ovIn;
        // Output
        int         nOutBufIndex;
        char        OutBuffer[MAXLINE];
        OVERLAPPED  ovOut;
        DWORD       dwWritten;
    };//
    // Global variables
    //HANDLE ghCompletionPort;//
    // Function prototypes
    //void CreateWorkerThreads();
    DWORD WINAPI ThreadFunc(LPVOID pvoid);
    void IssueRead(struct ContextKey *pCntx);
    void CheckOsVersion();
    void FatalError(char *s);///////////////////////////////////////////////////////int main(int argc, char *argv[])
    {
        SOCKET  listener;
        SOCKET  newsocket;
        WSADATA WsaData;
        struct sockaddr_in serverAddress;
        struct sockaddr_in clientAddress;
        int     clientAddressLength;
        int     err;    CheckOsVersion();    err = WSAStartup (0x0101, &WsaData);
        if (err == SOCKET_ERROR)
        {
            FatalError("WSAStartup Failed");
            return EXIT_FAILURE;
        }    /*
         * Open a TCP socket connection to the server
         * By default, a socket is always opened
         * for overlapped I/O.  Do NOT attach this
         * socket (listener) to the I/O completion
         * port!
         */
        listener = socket(AF_INET, SOCK_STREAM, 0);
        if (listener < 0)
        {
            FatalError("socket() failed");
            return EXIT_FAILURE;
        }    /*
         * Bind our local address
         */
        memset(&serverAddress, 0, sizeof(serverAddress));
        serverAddress.sin_family      = AF_INET;
        serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
        serverAddress.sin_port        = htons(SERV_TCP_PORT);    err = bind(listener,
                (struct sockaddr *)&serverAddress,
                sizeof(serverAddress)
              );
        if (err < 0)
            FatalError("bind() failed");    ghCompletionPort = CreateIoCompletionPort(
                INVALID_HANDLE_VALUE,
                NULL,   // No prior port
                0,      // No key
                0       // Use default  # of threads
                );
        if (ghCompletionPort == NULL)
            FatalError("CreateIoCompletionPort() failed");    CreateWorkerThreads(ghCompletionPort);    listen(listener, 5);    fprintf(stderr, "Echo Server with I/O Completion Ports\n");
        fprintf(stderr, "Running on TCP port %d\n", SERV_TCP_PORT);
        fprintf(stderr, "\nPress Ctrl+C to stop the server\n");    //
        // Loop forever accepting requests new connections
        // and starting reading from them.
        //
        for (;;)
        {
            struct ContextKey *pKey;        clientAddressLength = sizeof(clientAddress);
            newsocket = accept(listener,
                                (struct sockaddr *)&clientAddress,
                                &clientAddressLength);
            if (newsocket < 0)
            {
                FatalError("accept() Failed");
                return EXIT_FAILURE;
            }        // Create a context key and initialize it.
            // calloc will zero the buffer
            pKey = calloc(1, sizeof(struct ContextKey));
            pKey->sock = newsocket;
            pKey->ovOut.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
            // Set the event for writing so that packets
            // will not be sent to the completion port when
            // a write finishes.
            pKey->ovOut.hEvent = (HANDLE)((DWORD)pKey->ovOut.hEvent | 0x1);        // Associate the socket with the completion port
            CreateIoCompletionPort(
                    (HANDLE)newsocket,
                    ghCompletionPort,
                    (DWORD)pKey,   // No key
                    0              // Use default # of threads
                );        // Kick off the first read
            IssueRead(pKey);
        }
        return 0;
    }void CreateWorkerThreads()
    {
        SYSTEM_INFO  sysinfo;
        DWORD        dwThreadId;
        DWORD        dwThreads;
        DWORD        i;    GetSystemInfo(&sysinfo);
        dwThreads = sysinfo.dwNumberOfProcessors * 2 + 2;
        for (i=0; i<dwThreads; i++)
        {
            HANDLE hThread;
            hThread = CreateThread(
                NULL, 0, ThreadFunc, NULL, 0, &dwThreadId
                );
            CloseHandle(hThread);
        }
    }//
    // Each worker thread starts here.
      

  5.   

    //接上
    DWORD WINAPI ThreadFunc(LPVOID pVoid)
    {
        BOOL    bResult;
        DWORD   dwNumRead;
        struct ContextKey *pCntx;
        LPOVERLAPPED lpOverlapped;    UNREFERENCED_PARAMETER(pVoid);    // Loop forever on getting packets from
        // the I/O completion port.
        for (;;)
        {
            bResult = GetQueuedCompletionStatus(
                   ghCompletionPort,
                   &dwNumRead,
                   &(DWORD)pCntx,
                   &lpOverlapped,
                   INFINITE
                );        if (bResult == FALSE
                && lpOverlapped == NULL)
            {
                FatalError(
                    "ThreadFunc - Illegal call to GetQueuedCompletionStatus");
            }        else if (bResult == FALSE
                && lpOverlapped != NULL)
            {
                // This happens occasionally instead of
                // end-of-file. Not sure why.
                closesocket(pCntx->sock);
                free(pCntx);
                fprintf(stderr,
                    "ThreadFunc - I/O operation failed\n");
            }        else if (dwNumRead == 0)
            {
                closesocket(pCntx->sock);
                free(pCntx);
                fprintf(stderr, "ThreadFunc - End of file.\n");
            }        // Got a valid data block!
            // Save the data to our buffer and write it
            // all back out (echo it) if we have see a \n
            else
            {
                // Figure out where in the buffer to save the character
                char *pch = &pCntx->OutBuffer[pCntx->nOutBufIndex++];
                *pch++ = pCntx->InBuffer[0];
                *pch = '\0';    // For debugging, WriteFile doesn't care
                if (pCntx->InBuffer[0] == '\n')
                {
                    WriteFile(
                            (HANDLE)(pCntx->sock),
                            pCntx->OutBuffer,
                            pCntx->nOutBufIndex,
                            &pCntx->dwWritten,
                            &pCntx->ovOut
                        );
                    pCntx->nOutBufIndex = 0;
                    fprintf(stderr, "Echo on socket %x.\n", pCntx->sock);
                }            // Start a new read
                IssueRead(pCntx);
            }
        }    return 0;
    }
    /*
     * Call ReadFile to start an overlapped request
     * on a socket.  Make sure we handle errors
     * that are recoverable.
     */
    void IssueRead(struct ContextKey *pCntx)
    {
        int     i = 0;
        BOOL    bResult;
        int     err;
        int     numRead;    while (++i)
        {
            // Request a single character
            bResult = ReadFile(
                        (HANDLE)pCntx->sock,
                        pCntx->InBuffer,
                        1,
                        &numRead,
                        &pCntx->ovIn
                    );        // It succeeded immediately, but do not process it
            // here, wait for the completion packet.
            if (bResult)
                return;        err = GetLastError();        // This is what we want to happen, it's not an error
            if (err == ERROR_IO_PENDING)
                return;        // Handle recoverable error
            if ( err == ERROR_INVALID_USER_BUFFER ||
                 err == ERROR_NOT_ENOUGH_QUOTA ||
                 err == ERROR_NOT_ENOUGH_MEMORY )
            {
                if (i == 5) // I just picked a number
                {
                    Sleep(50);  // Wait around and try later
                    continue;
                }            FatalError("IssueRead - System ran out of non-paged space");
            }        break;
        }    fprintf(stderr, "IssueRead - ReadFile failed.\n");
    }//
    // Make sure we are running under the right versions
    // of Windows NT (3.51, 4.0, or later)
    //
    void CheckOsVersion()
    {
        OSVERSIONINFO   ver;
        BOOL            bResult;    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);    bResult = GetVersionEx((LPOSVERSIONINFO) &ver);    if ( (!bResult) ||
             (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )
        {
            FatalError("ECHOSRV requires Windows NT 3.51 or later.");
        }}//
    // Error handler
    //
    void FatalError(char *s)
    {
        fprintf(stdout, "%s\n", s);
        exit(EXIT_FAILURE);
    }
      

  6.   

    64个的限制是因为winsock.h中有#define fd_setsize 64这一行,你可以在包含winsock.h之前加入#define FD_SETSIZE xxx,就可以建立64个以上连接