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

📄 iocpserver.cpp

📁 这个是网络编程
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// 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 overlapped IO with a completion port for
//      TCP and UDP 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. 
//
//      For UDP, an echo socket is creatd for each IP address family available.
//      For each socket, several receives are posted. Once these receives 
//      complete, the data is sent back to the receiver.
//
//      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]
//          -p proto   Which protocol to use [default = TCP]
//              tcp         Use TCP
//              udp         Use UDP
//

#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_COMPLETION_THREAD_COUNT 32     // Maximum number of completion threads allowed

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,
    gOverlappedCount = DEFAULT_OVERLAPPED_COUNT;

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

//
// 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

    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;

    ULONG                IoOrder;       // Order in which this I/O was posted

    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        OutstandingOps; // Number of outstanding overlapped ops on 
                                         //    socket

    BUFFER_OBJ         **PendingAccepts; // Pending AcceptEx buffers 
                                         //   (used for listening sockets only)

    ULONG                LastSendIssued, // Last sequence number sent
                         IoCountIssued;  // Next sequence number assigned to receives
    BUFFER_OBJ          *OutOfOrderSends;// List of send buffers that completed out of order

    // Pointers to Microsoft specific extensions. These are used by listening
    //   sockets only
    LPFN_ACCEPTEX        lpfnAcceptEx;
    LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;

    CRITICAL_SECTION     SockCritSec;    // Protect access to this structure

    struct _SOCKET_OBJ  *next;
} SOCKET_OBJ;

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

//
// 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"
                    "  -p tcp|udp Which protocol to use [default = TCP]\n",
                    gBufferSize,
                    gBindPort
                    );
    ExitProcess(-1);
}

void dbgprint(char *format,...)
{
#ifdef DEBUG
    va_list vl;
    char    dbgbuf[2048];

    if (pid == 0)
    {
        pid = GetCurrentProcessId();
    }

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

    OutputDebugString(dbgbuf);
#endif
}

//
// Function: GetBufferObj
// 
// Description:
//    Allocate a BUFFER_OBJ. Each receive posted allocates one of these. 
//    After the recv is successful, the BUFFER_OBJ is queued for
//    sending by the send thread. To increase performance, a lookaside lists 
//    should be used to cache free BUFFER_OBJ.
//
BUFFER_OBJ *GetBufferObj(SOCKET_OBJ *sock, int buflen)
{
    BUFFER_OBJ *newobj=NULL;

    // Allocate the object
    newobj = (BUFFER_OBJ *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BUFFER_OBJ));
    if (newobj == NULL)
    {
        fprintf(stderr, "GetBufferObj: HeapAlloc failed: %d\n", GetLastError());
        ExitProcess(-1);
    }
    // Allocate the buffer
    newobj->buf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BYTE) *buflen);
    if (newobj->buf == NULL)
    {
        fprintf(stderr, "GetBufferObj: HeapAlloc failed: %d\n", GetLastError());
        ExitProcess(-1);
    }
    newobj->buflen = buflen;

    newobj->addrlen = sizeof(newobj->addr);

    return newobj;
}

//
// Function: FreeBufferObj
// 
// Description:
//    Free the buffer object. To increase performance, a lookaside list should be
//    implemented to cache BUFFER_OBJ when freed.
//
void FreeBufferObj(BUFFER_OBJ *obj)
{
    HeapFree(GetProcessHeap(), 0, obj->buf);
    HeapFree(GetProcessHeap(), 0, obj);
}

//
// Function: GetSocketObj
//
// Description:
//    Allocate a socket object and initialize its members. A socket object is
//    allocated for each socket created (either by socket or accept).
//    Again, a lookaside list can be implemented to cache freed SOCKET_OBJ to
//    improve performance.
//
SOCKET_OBJ *GetSocketObj(SOCKET s, int af)
{
    SOCKET_OBJ  *sockobj=NULL;

    sockobj = (SOCKET_OBJ *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKET_OBJ));
    if (sockobj == NULL)
    {
        fprintf(stderr, "GetSocketObj: HeapAlloc failed: %d\n", GetLastError());
        ExitProcess(-1);
    }

    // Initialize the members
    sockobj->s = s;
    sockobj->af = af;

    // For TCP we initialize the IO count to one since the AcceptEx is posted
    //    to receive data
    sockobj->IoCountIssued = ((gProtocol == IPPROTO_TCP) ? 1 : 0);

    InitializeCriticalSection(&sockobj->SockCritSec);

    return sockobj;
}

//
// Function: FreeSocketObj
//
// Description:
//    Frees a socket object. If there are outstanding operations, the object
//    is not freed. 
//
void FreeSocketObj(SOCKET_OBJ *obj)
{
    BUFFER_OBJ  *ptr=NULL,
                *tmp=NULL;

    if (obj->OutstandingOps != 0)
    {
        // Still outstanding operations so just return
        return;
    }
    // Close the socket if it hasn't already been closed
    if (obj->s != INVALID_SOCKET)
    {
        closesocket(obj->s);
        obj->s = INVALID_SOCKET;
    }

    DeleteCriticalSection(&obj->SockCritSec);

    HeapFree(GetProcessHeap(), 0, obj);
}

//
// Function: ValidateArgs
//
// Description:
//      Parses the command line arguments and sets up some global 
//      variables.
//
void ValidateArgs(int argc, char **argv)
{
    int     i;

    for(i=1; i < argc ;i++)
    {
        if (((argv[i][0] != '/') && (argv[i][0] != '-')) || (strlen(argv[i]) < 2))
            usage(argv[0]);
        else
        {
            switch (tolower(argv[i][1]))
            {
                case 'a':               // address family - IPv4 or IPv6
                    if (i+1 >= argc)
                        usage(argv[0]);
                    if (argv[i+1][0] == '4')
                        gAddressFamily = AF_INET;
                    else if (argv[i+1][0] == '6')
                        gAddressFamily = AF_INET6;
                    else
                        usage(argv[0]);
                    i++;
                    break;
                case 'b':               // buffer size for send/recv
                    if (i+1 >= argc)
                        usage(argv[0]);
                    gBufferSize = atol(argv[++i]);
                    break;
                case 'e':               // endpoint - port number
                    if (i+1 >= argc)
                        usage(argv[0]);
                    gBindPort = argv[++i];
                    break;
                case 'l':               // local address for binding
                    if (i+1 >= argc)
                        usage(argv[0]);
                    gBindAddr = argv[++i];
                    break;
                case 'o':               // overlapped count
                    if (i+1 >= argc)
                        usage(argv[0]);
                    gOverlappedCount = atol(argv[++i]);
                    break;
                case 'p':               // protocol - TCP or UDP
                    if (i+1 >= argc)
                        usage(argv[0]);
                    if (_strnicmp(argv[i+1], "tcp", 3) == 0)
                    {
                        gProtocol = IPPROTO_TCP;
                        gSocketType = SOCK_STREAM;
                    }
                    else if (_strnicmp(argv[i+1], "udp", 3) == 0)
                    {
                        gProtocol = IPPROTO_UDP;
                        gSocketType = SOCK_DGRAM;
                    }
                    else
                        usage(argv[0]);
                    i++;
                    break;
                default:
                    usage(argv[0]);
                    break;
            }
        }
    }
}

//
// Function: PrintStatistics
//
// Description:
//    Print the send/recv statistics for the server
//
void PrintStatistics()
{
    ULONG       bps, tick, elapsed;

    tick = GetTickCount();

    elapsed = (tick - gStartTime) / 1000;

    if (elapsed == 0)
        return;

    printf("\n");

    // Calculate average bytes per second
    bps = gBytesSent / elapsed;
    printf("Average BPS sent: %lu [%lu]\n", bps, gBytesSent);

    bps = gBytesRead / elapsed;
    printf("Average BPS read: %lu [%lu]\n", bps, gBytesRead);

    elapsed = (tick - gStartTimeLast) / 1000;

    if (elapsed == 0)
        return;

    // Calculate bytes per second over the last X seconds
    bps = gBytesSentLast / elapsed;
    printf("Current BPS sent: %lu\n", bps);

    bps = gBytesReadLast / elapsed;
    printf("Current BPS read: %lu\n", bps);

    InterlockedExchange(&gBytesSentLast, 0);
    InterlockedExchange(&gBytesReadLast, 0);

    gStartTimeLast = tick;
}

//
// Function: PostRecv
// 
// Description: 
//    Post an overlapped receive operation on the socket.
//

⌨️ 快捷键说明

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