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

📄 iocpclient.cpp

📁 这个是网络编程
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// Sample: I/O Completion Port IPv4/IPv6 Client
//
// Files:
//      iocpclient.cpp    - this file
//      resolve.cpp       - Common name resolution routines
//      resolve.h         - Header file for name resolution routines
//
// Description:
//      This is a sample client which uses IO completion ports and
//      uses the ConnectEx and TransmitFile APIs. As such this client
//      only works on Windows XP+. This client will attempt the specified
//      number of connections for each server address resovled. That is
//      if the server's address is explicitly specified (via the -c NUM 
//      option) then the client will establish NUM connections. If the
//      server's hostname is specified and it resolves to 5 different
//      addresses then NUM connections are attemped for each address.
//
//      Again, this client is an echo client. It sends a specified amount
//      of data on the connection and then waits to receive the data
//      back. Normally, the client uses WSASend for data, but if the
//      -t SIZE flag is given then TransmitFile is used with a temporary
//      file of SIZE bytes. This temp file is created on the fly and
//      deleted upon client exit.
//
//      The -r command is used for basic rate limiting of data transfers
//      from the client to the server. For reasonable limits the mechanism
//      works; however, the higher the limit is the less reliable this method
//      becomes (as the sleep time approaches zero - then the time it takes
//      to post the sends becomes an issue).  This option is provided to
//      allow a method of simulating a steady connection rate (as the 
//      connection requests are shaped as well) in addition to smoothing the
//      data transmission to the server to prevent sudden spikes and/or
//      saturating the network bandwidth.
//
//      NOTE: This client only supports the TCP protocol.
// 
// Compile:
//      cl -o iocpclient.exe iocpclient.cpp resolve.cpp ws2_32.lib
//
// Usage:
//      iocpclient.exe [options]
//          -a 4|6     Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]
//          -b size    Buffer size for send/recv (in bytes)
//          -c count   Number of connections to establish
//          -e port    Port number
//          -n server  Server address or name to connect to
//          -l addr    Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]
//          -r rate    Rate at which to send data
//          -t size    Use TransmitFile instead of sends (size of file to send)
//          -x count   Number of sends
//

#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    1      // Number of overlapped recv per socket
#define DEFAULT_CLIENT_CONNECTIONS  10     // Number of connections to initiate
#define DEFAULT_FILE_SIZE           2000000// Default size of file for TransmitFile
#define DEFAULT_SEND_COUNT          100    // How many send/TransmitFiles to perform

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,
    gConnectionCount = DEFAULT_CLIENT_CONNECTIONS,
    gFileSize        = DEFAULT_FILE_SIZE,
    gSendCount       = DEFAULT_SEND_COUNT,
    gRateLimit       = -1,
    gTimeout         = 0;

USHORT gLocalPort = 0x0000FFFD;

BOOL gTransmitFile = FALSE;             // Use TransmitFile instead

HANDLE gTempFile   = INVALID_HANDLE_VALUE;

char *gBindAddr    = NULL,              // local interface to bind to
     *gServerAddr  = NULL,              // Server address to connect 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;

    HANDLE               hFile;         // Open file handle for TransmitFile

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

    int                  operation;     // Type of operation issued
#define OP_CONNECT      0                   // ConnectEx
#define OP_READ         1                   // WSARecv/WSARecvFrom
#define OP_WRITE        2                   // WSASend/WSASendTo
#define OP_TRANSMIT     3                   // TransmitFile

    SOCKADDR_STORAGE     addr;
    int                  addrlen;

    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)

    volatile LONG        OutstandingOps; // Number of outstanding overlapped ops on 
                                         //    socket
    volatile LONG        SendCount;      // Number of sends to perform on connection

    BOOL                 bConnected;
    BOOL                 bClosing;       // Is socket closing

    // Pointers to Microsoft specific extensions.
    LPFN_CONNECTEX       lpfnConnectEx;
    LPFN_TRANSMITFILE    lpfnTransmitFile;

    BUFFER_OBJ          *Repost;         // Send buffer to repost (used with rate limit)

    CRITICAL_SECTION     SockCritSec;

    struct _SOCKET_OBJ  *next,
                        *prev;
} SOCKET_OBJ;

// List of open sockets connected to server
SOCKET_OBJ *gConnectionList=NULL;

//
// Statistics counters
//
volatile LONG gBytesRead=0,
              gBytesSent=0,
              gStartTime=0,
              gBytesReadLast=0,
              gBytesSentLast=0,
              gStartTimeLast=0,
              gTotalConnections=0,
              gCurrentConnections=0,
              gConnectionRefused=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"
                    "  -c count   Number of connections to establish\n"
                    "  -e port    Port number [default = %s]\n"
                    "  -n server  Server address or name to connect to\n"
                    "  -p port    Local port number to bind to\n"
                    "  -l addr    Local address to bind to [default INADDR_ANY for IPv4 or INADDR6_ANY for IPv6]\n"
                    "  -r rate    Use the QOS provider to limit send rate\n"
                    "  -t size    Use TransmitFile instead of sends (size of file to send)\n"
                    "  -x count   Number of sends\n",
                    gBufferSize,
                    gBindPort
                    );
    ExitProcess(-1);
}

//
// 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(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);
    }

    InitializeCriticalSection(&sockobj->SockCritSec);

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

    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;
    }

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

//
// Function: InsertSocketObj
//
// Description:
//    Insert a SOCKET_OBJ into a list of socket objects. Insertions
//    are performed at the end of the list.
//
void InsertSocketObj(SOCKET_OBJ **head, SOCKET_OBJ *obj)
{
    SOCKET_OBJ *end=NULL,
               *ptr=NULL;

    // Find the end of the list
    ptr = *head;
    if (ptr)
    {
        while (ptr->next)
            ptr = ptr->next;
        end = ptr;
    }

    obj->next = NULL;
    obj->prev = end;

    if (end == NULL)
    {
        // List is empty
        *head = obj;
    }
    else
    {
        // Put new object at the end
        end->next = obj;
        obj->prev = end;
    }
}

//
// Function: RemoveSocketObj
//
// Description:
//    Removes the specified SOCKET_OBJ from the list of socket objects.
//
SOCKET_OBJ *RemoveSocketObj(SOCKET_OBJ **head, SOCKET_OBJ *buf)
{
    // Make sure list isn't empty
    if (*head != NULL)
    {
        // Fix up the next and prev pointers
        if (buf->prev)
            buf->prev->next = buf->next;
        if (buf->next)
            buf->next->prev = buf->prev;

        if (*head == buf)
            (*head) = buf->next;
    }
    return buf;
}

//
// Function: CreateTempFile
//
// Description:
//    Creates a temporary file which is used by all TransmitFile
//    operations. This file is created as temporary and will be
//    deleted upon handle closure.
//
HANDLE CreateTempFile(char *filename, DWORD size)
{
    OVERLAPPED ol;
    HANDLE     hFile;
    DWORD      bytes2write,
               offset,
               nLeft,
               written,
               buflen=1024;
    char       buf[1024];
    int        rc;

    // Create the file as temporary.
    hFile = CreateFile(
            filename,
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_OVERLAPPED | 
                FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_DELETE_ON_CLOSE,
            NULL
            );
    if (hFile == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "CreateTempFile failed: %d\n", GetLastError());
        return hFile;
    }

    memset(buf, '$', buflen);

    memset(&ol, 0, sizeof(ol));
    offset = 0;

    ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (ol.hEvent == FALSE)
    {
        fprintf(stderr, "CreateTempFile: CreateEvent failed: %d\n",
                GetLastError());
        return INVALID_HANDLE_VALUE;
    }

    // Write the specified amount of data to the file
    nLeft = size;
    while (nLeft > 0)
    {
        bytes2write = ((nLeft < buflen) ? nLeft : buflen);
        
        ol.Offset = offset;

        rc = WriteFile(
                hFile,
                buf,
                bytes2write,
               &written,
               &ol
                );
        if (rc == 0)
        {
            if (GetLastError() != ERROR_IO_PENDING)
            {
                fprintf(stderr, "CreateTempFile: WriteFile failed: %d\n",
                        GetLastError());
                return INVALID_HANDLE_VALUE;
            }
            else
            {
                rc = GetOverlappedResult(
                        hFile,
                       &ol,
                       &written,
                        TRUE
                        );
                if (rc == 0)
                {
                    fprintf(stderr, "CreateTempFile: GetOverlappedResult failed: %d\n",
                            GetLastError());
                    return INVALID_HANDLE_VALUE;
                }
            }
        }
        ResetEvent(ol.hEvent);

        // Ajust the offset and bytes left to write
        offset += written;
        nLeft  -= written;
    }

    printf("Created temp file of size: %d\n", offset);

    CloseHandle(ol.hEvent);

    return hFile;
}

//
// 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]);

⌨️ 快捷键说明

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