⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iocpserver.cpp

📁 这个是网络编程
💻 CPP
📖 第 1 页 / 共 4 页
字号:
//
// Sample: I/O Completion Port IPv4/IPv6 Server
//
// Files:
//      iocpserver.cpp    - this file
//      resolve.cpp       - Common name resolution routines
//      resolve.h         - Header file for name resolution routines
//
// Description:
//      This sample illustrates how to write a scalable, high-performance
//      Winsock server. This is implemented as a TCP (IPv4/IPv6) server
//      designed to handle many connections simultaneously. The purpose
//      of this server is an echo server. For each accepted connection,
//      data is read and then sent back to the client. Several limitations
//      are present to ensure that as many concurrent connections can
//      be handled. First, each connection has only a single overlapped
//      receive posted at any given time. Second, each connection may only
//      have a maximum of five outstanding overlapped sends. This prevents
//      a malicious client from connecting and only sending data -- this
//      would cause an unlimited number of data to be buffered since the
//      sends would block as the client is not receiving data (and the TCP
//      window size goes to zero).
//
//      This sample illustrates overlapped IO with a completion port for
//      TCP over both IPv4 and IPv6. This sample uses the 
//      getaddrinfo/getnameinfo APIs which allows this application to be 
//      IP version independent. That is the desired address family 
//      (AF_INET or AF_INET6) can be determined simply from the string 
//      address passed via the -l command.
//
//      For TCP, a listening socket is created for each IP address family
//      available. Each socket is associated with a completion port and
//      worker threads are spawned (one for each CPU available). For each
//      listening thread, a number of AcceptEx are posted. The worker threads
//      then wait for one of these to complete. Upon completion, the client
//      socket is associated with the completion port and several receives
//      are posted. The AcceptEx is reposted as well. Once data is received
//      on a client socket, it is echoed back. 
//
//      The important thing to remember with IOCP is that the completion events
//      may occur out of order; however, the buffers are guaranteed to be filled
//      in the order posted. For our echo server this can cause problems as 
//      receive N+1 may complete before receive N. We can't echo back N+1 before
//      echoing N. There are two approaches possible. First, we could surmise
//      that since receive N+1 has completed then we can safely echo back receive
//      N and N+1 at that time (to maintain the data ordering). To do this properly
//      you'll have to call WSAGetOverlappedResult on receive N in order to find
//      out how many bytes were received to echo it back. The second approach
//      (which is implemented in this sample) is to keep a list of receive
//      buffers that completed out of order. This list is maintained in the
//      per-socket data structure. When receive N+1 completes, it will notice that
//      receive N has not completed. The buffer is then queued in the out of
//      order send list. Once receive N completes, its buffer is queued -- the
//      queue is ordered in the same order that the receive operations are.
//      Another routine (DoSends) goes through this list and sends those buffers
//      that are available and in order. If any gaps are detected no further buffers
//      are sent (as we will wait for that receive to complete and insert its
//      buffer into the list so that the next call to DoSends will correctly
//      send the buffers in the right order).
//
//      For example:
//          If this sample is called with the following command lines:
//              iocpserver.exe -l fe80::2efe:1234 -e 5150
//              iocpserver.exe -l ::
//          Then the server creates an IPv6 socket as an IPv6 address was
//          provided.
//
//          On the other hand, with the following command line:
//              iocpserver.exe -l 7.7.7.1 -e 5150
//              iocpserver.exe -l 0.0.0.0
//          Then the server creates an IPv4 socket.
//
//          Calling the server with no parameters will create a server that
//          listens both IPv4 and IPv6 (if installed).
//
// Compile:
//      cl -o iocpserver.exe iocpserver.cpp resolve.cpp ws2_32.lib
//
// Usage:
//      iocpserver.exe [options]
//          -a 4|6     Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]
//          -b size    Buffer size for send/recv
//          -e port    Port number
//          -l addr    Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]
//          -os count  Maximum number of overlapped send operations to allow simultaneously (per socket)
//          -oa count  Maximum number of overlapped accepts to allow simultaneously
//          -o  count  Number of initial overlapped accepts to post
//

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#include "resolve.h"

#define DEFAULT_BUFFER_SIZE         4096   // default buffer size
#define DEFAULT_OVERLAPPED_COUNT    5      // Number of overlapped recv per socket
#define MAX_OVERLAPPED_ACCEPTS      500
#define MAX_OVERLAPPED_SENDS        200
#define MAX_OVERLAPPED_RECVS        200
#define MAX_COMPLETION_THREAD_COUNT 32     // Maximum number of completion threads allowed

#define BURST_ACCEPT_COUNT          100

int gAddressFamily = AF_UNSPEC,         // default to unspecified
    gSocketType    = SOCK_STREAM,       // default to TCP socket type
    gProtocol      = IPPROTO_TCP,       // default to TCP protocol
    gBufferSize    = DEFAULT_BUFFER_SIZE,
    gInitialAccepts= DEFAULT_OVERLAPPED_COUNT,
    gMaxAccepts    = MAX_OVERLAPPED_ACCEPTS,
    gMaxReceives   = MAX_OVERLAPPED_RECVS,
    gMaxSends      = MAX_OVERLAPPED_SENDS;

char *gBindAddr    = NULL,              // local interface to bind to
     *gBindPort    = "5150";            // local port to bind to

//
// Statistics counters
//
volatile LONG gBytesRead=0,
              gBytesSent=0,
              gStartTime=0,
              gBytesReadLast=0,
              gBytesSentLast=0,
              gStartTimeLast=0,
              gConnections=0,
              gConnectionsLast=0,
              gOutstandingSends=0;


//
// This is our per I/O buffer. It contains a WSAOVERLAPPED structure as well
//    as other necessary information for handling an IO operation on a socket.
//
typedef struct _BUFFER_OBJ
{
    WSAOVERLAPPED        ol;

    SOCKET               sclient;       // Used for AcceptEx client socket
    HANDLE               PostAccept;

    char                *buf;           // Buffer for recv/send/AcceptEx
    int                  buflen;        // Length of the buffer

    int                  operation;     // Type of operation issued
#define OP_ACCEPT       0                   // AcceptEx
#define OP_READ         1                   // WSARecv/WSARecvFrom
#define OP_WRITE        2                   // WSASend/WSASendTo

    SOCKADDR_STORAGE     addr;
    int                  addrlen;

    struct _SOCKET_OBJ  *sock;

    struct _BUFFER_OBJ  *next;

} BUFFER_OBJ;

//
// This is our per socket buffer. It contains information about the socket handle
//    which is returned from each GetQueuedCompletionStatus call.
//
typedef struct _SOCKET_OBJ
{
    SOCKET             s;               // Socket handle

    int                af,              // Address family of socket (AF_INET, AF_INET6)
                       bClosing;        // Is the socket closing?

    volatile LONG      OutstandingRecv, // Number of outstanding overlapped ops on 
                       OutstandingSend,
                       PendingSend;


    CRITICAL_SECTION   SockCritSec;     // Protect access to this structure

    struct _SOCKET_OBJ  *next;
} SOCKET_OBJ;

//
//
//
typedef struct _LISTEN_OBJ
{
    SOCKET          s;

    int             AddressFamily;

    BUFFER_OBJ     *PendingAccepts; // Pending AcceptEx buffers 
                               
    volatile long   PendingAcceptCount;

    int             HiWaterMark,
                    LoWaterMark;

    HANDLE          AcceptEvent;
    HANDLE          RepostAccept;
    volatile long   RepostCount;

    // Pointers to Microsoft specific extensions.
    LPFN_ACCEPTEX             lpfnAcceptEx;
    LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;

    CRITICAL_SECTION ListenCritSec;

    struct _LISTEN_OBJ *next;
} LISTEN_OBJ;

// Serialize access to the free lists below
CRITICAL_SECTION gBufferListCs,
                 gSocketListCs,
                 gPendingCritSec;

// Lookaside lists for free buffers and socket objects
BUFFER_OBJ *gFreeBufferList=NULL;
SOCKET_OBJ *gFreeSocketList=NULL;

BUFFER_OBJ *gPendingSendList=NULL,
           *gPendingSendListEnd=NULL;

int  PostSend(SOCKET_OBJ *sock, BUFFER_OBJ *sendobj);
int  PostRecv(SOCKET_OBJ *sock, BUFFER_OBJ *recvobj);
void FreeBufferObj(BUFFER_OBJ *obj);

//
// Function: usage
//
// Description:
//      Prints usage information and exits the process.
//
void usage(char *progname)
{
    fprintf(stderr, "usage: %s [-a 4|6] [-e port] [-l local-addr] [-p udp|tcp]\n",
            progname);
    fprintf(stderr, "  -a  4|6     Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]\n"
                    "  -b  size    Buffer size for send/recv [default = %d]\n"
                    "  -e  port    Port number [default = %s]\n"
                    "  -l  addr    Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]\n"
                    "  -oa count   Maximum overlapped accepts to allow\n"
                    "  -os count   Maximum overlapped sends to allow\n"
                    "  -or count   Maximum overlapped receives to allow\n"
                    "  -o  count   Initial number of overlapped accepts to post\n",
                    gBufferSize,
                    gBindPort
                    );
    ExitProcess(-1);
}

//
// Function: dbgprint
//
// Description:
//    Prints a message if compiled with the DEBUG flag.
//
void dbgprint(char *format,...)
{
#ifdef DEBUG
    va_list vl;
    char    dbgbuf[2048];

    va_start(vl, format);
    wvsprintf(dbgbuf, format, vl);
    va_end(vl);

    printf(dbgbuf);
    OutputDebugString(dbgbuf);
#endif
}

//
// Function: EnqueuePendingOperation
//
// Description:
//    Enqueues a buffer object into a list (at the end).
//
void EnqueuePendingOperation(BUFFER_OBJ **head, BUFFER_OBJ **end, BUFFER_OBJ *obj, int op)
{
    EnterCriticalSection(&gPendingCritSec);

    if (op == OP_READ)
        ;
    else if (op == OP_WRITE)
        InterlockedIncrement(&obj->sock->PendingSend);

    obj->next = NULL;
    if (*end)
    {
        (*end)->next = obj;
        (*end) = obj;
    }
    else
    {
        (*head) = (*end) = obj;
    }
    LeaveCriticalSection(&gPendingCritSec);

    return;
}

//
// Function: DequeuePendingOperation
//
// Description:
//    Dequeues the first entry in the list.
//
BUFFER_OBJ *DequeuePendingOperation(BUFFER_OBJ **head, BUFFER_OBJ **end, int op)
{
    BUFFER_OBJ *obj=NULL;

    EnterCriticalSection(&gPendingCritSec);

    if (*head)
    {
        obj = *head;

        (*head) = obj->next;

        // If next is NULL, no more objects are in the queue
        if (obj->next == NULL)
        {
            (*end) = NULL;
        }

        if (op == OP_READ)
            ;
        else if (op == OP_WRITE)
            InterlockedDecrement(&obj->sock->PendingSend);
    }

    LeaveCriticalSection(&gPendingCritSec);

    return obj;
}

//
// Function: ProcessPendingOperations
//
// Description:
//    This function goes through the list of pending send operations and posts them
//    as long as the maximum number of ouststanding sends is not exceeded.
//
void ProcessPendingOperations()
{
    BUFFER_OBJ *sendobj=NULL;

    while(gOutstandingSends < gMaxSends)
    {
        sendobj = DequeuePendingOperation(&gPendingSendList, &gPendingSendListEnd, OP_WRITE);
        if (sendobj)
        {

            if (PostSend(sendobj->sock, sendobj) == SOCKET_ERROR)
            {
                // Cleanup
                printf("ProcessPendingOperations: PostSend failed!\n");

                FreeBufferObj(sendobj);

                break;
            }
        }
        else
        {
            break;
        }
    }

    return;
}

//
// Function: InsertPendingAccept
//
// Description:
//    Inserts a pending accept operation into the listening object.
//
void InsertPendingAccept(LISTEN_OBJ *listenobj, BUFFER_OBJ *obj)
{
    obj->next = NULL;

    EnterCriticalSection(&listenobj->ListenCritSec);
    if (listenobj->PendingAccepts == NULL)
    {
        listenobj->PendingAccepts = obj;
    }
    else
    {
        // Insert at head - order doesn't really matter
        obj->next = listenobj->PendingAccepts;
        listenobj->PendingAccepts = obj;
    }
    LeaveCriticalSection(&listenobj->ListenCritSec);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -