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

📄 overserver.cpp

📁 这个是网络编程
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// Sample: Overlapped IPv4/IPv6 Server
//
// Files:
//      overserver.cpp    - this file
//      resolve.cpp       - Common name resolution routines
//      resolve.h         - Header file for name resolution routines
//
// Description:
//      This sample illustrates simple overlapped IO for TCP and UDP for
//      both IPv4 and IPv6. This sample uses the getaddrinfo/getnameinfo
//      APIs which allows this application to be IP agnostic. 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, this sample creates a listening socket for each address family 
//      available.  For each socket, a number of AcceptEx are posted.
//      We use one event handle for each overlapped operation. This means
//      that we assign an individual I/O operation to each worker thread 
//      at a time (not on a socket by socket basis). As such, it is possible
//      that some reads on a socket may be handled by one worker thread and
//      some other reads and writes for the same socket may be handled by 
//      another thread.
//
//      Once an AcceptEx completes, a new socket object is created which 
//      initiates a number of overlapped receives on the accepted connection.
//      Once a read completes, a write is posted back to that client and the
//      read is re-posted.
//
//      For UDP, a socket is created for each IP address family; however, 
//      a number of reads are immediately posted on each instead of accepts.
//      Once the UDP read is completed, a send back to the source is posted
//      and the receive is re-posted.
//      
//      For example:
//          If this sample is called with the following command lines:
//              overserver.exe -l fe80::2efe:1234 -e 5150
//              overserver.exe -l ::
//          Then the server creates an IPv6 socket as an IPv6 address was
//          provided.
//
//          On the other hand, with the following command line:
//              overserver.exe -l 7.7.7.1 -e 5150
//              overserver.exe -l 0.0.0.0
//          Then the server creates an IPv4 socket.
//          
//          Calling this sample with no arguments creates both IPv4 and IPv6
//              (if installed) servers.
//
// Compile:
//      cl -o overserver.exe overserver.cpp resolve.cpp ws2_32.lib
//
// Usage:
//      overserver.exe [options]
//          -a 4|6     Address family, 4 = IPv4, 6 = IPv6 [default = IPv4]
//          -b size    Size of send/recv buffer in bytes
//          -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 protocol
//             udp         Use UDP protocol
//

#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      // default number of overlapped recvs to post

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

struct _SOCKET_OBJ;
struct _THREAD_OBJ;

//
// This is our per I/O data. It describes a single I/O operation.
//
typedef struct _BUFFER_OBJ
{
    WSAOVERLAPPED        ol;            // Overlapped structure

    SOCKET               sclient;       // Used for AcceptEx client socket

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

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

    struct _SOCKET_OBJ *Socket;         // SOCKET_OBJ that this I/O belongs to
    struct _THREAD_OBJ *Thread;         // THREAD_OBJ this I/O is assigned to

    SOCKADDR_STORAGE     addr;          // Remote address (UDP)
    int                  addrlen;       // Remote address length

    struct _BUFFER_OBJ *next,
                       *prev;
} BUFFER_OBJ;

//
// This is our per handle data. One of these structures is allocated for
//    each socket created by our server.
//
typedef struct _SOCKET_OBJ
{
    SOCKET               s;             // Socket handle for client connection

    int                  af,            // Address family of socket (AF_INET or AF_INET6)
                         bClosing;      // Indicates socket is closing

    volatile LONG        OutstandingOps;    // Number of outstanding overlapped ops

    BUFFER_OBJ         **PendingAccepts;    // Array of pending AcceptEx calls (listening socket only)

    // Pointers to Microsoft specific extensions (listening socket only)
    LPFN_ACCEPTEX        lpfnAcceptEx;
    LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;

    CRITICAL_SECTION     SockCritSec;   // Synchronize access to this SOCKET_OBJ

    struct _SOCKET_OBJ  *next;          // Used to chain SOCKET_OBJ together
} SOCKET_OBJ;

//
// Allocated for each thread spawned. Each overlapped I/O issued on a socket is
//    assigned to one of the threads in the thread pool.
//
typedef struct _THREAD_OBJ
{
    BUFFER_OBJ *BufferList;            // Linked list of all sockets allocated

    int         EventCount;            // How many events are in the array to wait on?

    HANDLE      Event;                 // Used to signal new clients assigned
                                       //  to this thread
    HANDLE      Thread;                // Handle to the curren thread

    HANDLE      Handles[MAXIMUM_WAIT_OBJECTS]; // Array of socket's event handles

    CRITICAL_SECTION ThreadCritSec;    // Protect access to SOCKET_OBJ lists

    struct _THREAD_OBJ *next;          // Next thread object in list
} THREAD_OBJ;

THREAD_OBJ *gChildThreads=NULL;        // List of thread objects allocated
int         gChildThreadsCount=0;      // Number of child threads created

CRITICAL_SECTION gThreadListCritSec;

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

//
// Prototypes
//
void AssignIoToThread(BUFFER_OBJ *buf);
void RemoveBufferFromThread(SOCKET_OBJ *sock, BUFFER_OBJ *buf);
void InsertBufferObj(BUFFER_OBJ **head, 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"
                    "  -p tcp|udp Which protocol to use [default = TCP]\n",
                    gBufferSize,
                    gBindPort
                    );
    ExitProcess(-1);
}

//
// Function: GetBufferObj
// 
// Description:
//    Allocate a BUFFER_OBJ. Each send, receive, and accept posted by a 
//    by the server uses one of these objects. That is, there is one BUFFER_OBJ
//    allocated per I/O operation. After the I/O is initiated it is assigned to
//    one of the completion threads. To increase performance, a look aside list
//    may be used to cache freed 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);

    newobj->Socket = sock;

    // Create the event that is to be signed upon completion
    newobj->ol.hEvent = WSACreateEvent();
    if (newobj->ol.hEvent == NULL)
    {
        fprintf(stderr, "GetBufferObj: WSACreateEvent failed: %d\n", WSAGetLastError());
        ExitProcess(-1);
    }

    return newobj;
}

//
// Function: FreeBufferObj
// 
// Description:
//    Free the buffer object.
//
void FreeBufferObj(BUFFER_OBJ *obj)
{
    // Close the event
    WSACloseEvent(obj->ol.hEvent);
    obj->ol.hEvent = NULL;
    // Free the buffers
    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).
//
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;

    InitializeCriticalSection(&sockobj->SockCritSec);

    return sockobj;
}

//
// Function: FreeSocketObj
//
// Description:
//    Frees a socket object along.
//
void FreeSocketObj(SOCKET_OBJ *obj)
{
    BUFFER_OBJ  *ptr=NULL,
                *tmp=NULL;


    if (obj->OutstandingOps != 0)
    {
        return;
    }
    if (obj->s != INVALID_SOCKET)
    {
        closesocket(obj->s);
        obj->s = INVALID_SOCKET;
    }

    DeleteCriticalSection(&obj->SockCritSec);

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

//
// Function: GetThreadObj
//
// Description:
//    Allocate a thread object and initializes its members.
//
THREAD_OBJ *GetThreadObj()
{
    THREAD_OBJ *thread=NULL;

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

    // Create an event that the thread will wait on. When signaled, the thread
    //    rebuilds the array of event handles.
    thread->Event = WSACreateEvent();
    if (thread->Event == NULL)
    {
        fprintf(stderr, "GetThreadObj: WSACreateEvent failed: %d\n", WSAGetLastError());
        ExitProcess(-1);
    }

    // The first event is always the thread's event
    thread->Handles[0] = thread->Event;

    thread->EventCount = 1;

    InitializeCriticalSection(&thread->ThreadCritSec);

    return thread;
}

//
// Function: FreeThreadObj
//
// Description:
//    Free a thread object and is member fields.
//
void FreeThreadObj(THREAD_OBJ *thread)
{
    WSACloseEvent(thread->Event);

    CloseHandle(thread->Thread);

    DeleteCriticalSection(&thread->ThreadCritSec);

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

//
// Function: InsertBufferObjToThread
//
// Description:
//    Insert a buffer object into the list of pending buffers objects for
//    the given thread object. If the buffer can fit in the thread's queue,
//    NO_ERROR is returned. If the thread is already waiting on the maximum
//    allowable events, then SOCKET_ERROR is returned.
//
int InsertBufferObjToThread(THREAD_OBJ *thread, BUFFER_OBJ *buf)
{
    int     ret;

    EnterCriticalSection(&thread->ThreadCritSec);

    // See if the thread is full
    if (thread->EventCount < MAXIMUM_WAIT_OBJECTS-1)
    {
        InsertBufferObj(&thread->BufferList, buf);

        thread->Handles[thread->EventCount++] = buf->ol.hEvent;

        ret = NO_ERROR;
    }
    else
    {
        ret = SOCKET_ERROR;
    }

    LeaveCriticalSection(&thread->ThreadCritSec);

    return ret;
}

//
// Function: RenumberEvents
//
// Description:
//    This routine goes through the list of pending buffers within a thread
//    and rebuilds the array of event handles that the thread waits on. When
//    a new connection is accepted and several receive operations are posted,
//    they are assigned to a thread and the thread is signaled to indicate
//    new I/O has been placed in its queue. The thread needs to reinitialize
//    its array so that it may be signaled for completion on that new I/O.
//
void RenumberEvents(THREAD_OBJ *thread)
{
    BUFFER_OBJ *bptr=NULL;
    int         i;

    //
    // If index 0 is signaled then rebuild the array of event
    //    handles to wait on
    EnterCriticalSection(&thread->ThreadCritSec);
    i = 0;
    bptr = thread->BufferList;
    thread->EventCount  = 1;
    while (bptr)
    {
        thread->Handles[thread->EventCount++] = bptr->ol.hEvent;

        i++;
        bptr = bptr->next;
    }
    LeaveCriticalSection(&thread->ThreadCritSec);
}

//
// Function: InsertBufferObj
//
// Description:
//   This routine inserts a BUFFER_OBJ into a list of BUFFER_OBJs.
//   First the end of the list is found and then the new buffer is
//   added to the end.
//
void InsertBufferObj(BUFFER_OBJ **head, BUFFER_OBJ *obj)
{
    BUFFER_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: RemoveBufferObj
//
// Description:
//    Remove a BUFFER_OBJ from the list.
//
BUFFER_OBJ *RemoveBufferObj(BUFFER_OBJ **head, BUFFER_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;

⌨️ 快捷键说明

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