📄 overserver.cpp
字号:
//
// 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 + -