📄 eventserver.cpp
字号:
//
// Sample: WSAEventSelect IPv4/IPv6 Server
//
// Files:
// eventserver.cpp - this file
// resolve.cpp - routines for resovling addresses, etc.
// resolve.h - header file for resolve.c
//
// Description:
// This sample illustrates the WSAEventSelect 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.
//
// Because of the limitation of waiting on a maximum of 64 events
// at a time, this sample uses a thread pool to service client
// connections. For TCP, a listening socket is created for each
// accepted connection which registers for FD_ACCEPT notifications.
// These sockets are assigned to a worker thread. Once a client
// connection is established, read and write events are registered
// and that socket is assigned to a worker thread as well. Once
// a thread is waiting on the maximum events allowed, a new worker
// thread will be created for additional connections, etc. For each
// connection, data is read and then added to a send queue for that
// connection. When data may be sent on the socket it is echoed back
// to the client.
//
// For UDP, this setup is similar except that only a single UDP socket
// is created for each address family available.
//
// For example:
// If this sample is called with the following command lines:
// eventserver.exe -l fe80::2efe:1234 -e 5150
// eventserver.exe -l ::
// Then the server creates an IPv6 socket as an IPv6 address was
// provided.
//
// On the other hand, with the following command line:
// eventserver.exe -l 7.7.7.1 -e 5150
// eventserver.exe -l 0.0.0.0
// Then the server creates an IPv4 socket.
//
// Compile:
// cl.exe -o eventserver.exe eventserver.cpp resolve.cpp ws2_32.lib
//
// Usage:
// asyncserver.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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
#define DEFAULT_BUFFER_SIZE 4096 // default buffer size
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;
char *gBindAddr = NULL, // local interface to bind to
*gBindPort = "5150"; // local port to bind to
//
// Allocated for each receiver posted
//
typedef struct _BUFFER_OBJ
{
char *buf; // Data buffer for data
int buflen; // Length of buffer or number of bytes contained in buffer
SOCKADDR_STORAGE addr; // Address data was received from (UDP)
int addrlen; // Length of address
struct _BUFFER_OBJ *next; // Used to maintain a linked list of buffers
} BUFFER_OBJ;
//
// Allocated for each socket handle
//
typedef struct _SOCKET_OBJ
{
SOCKET s; // Socket handle
HANDLE event; // Event handle
int listening; // Socket is a listening socket (TCP)
int closing; // Indicates whether the connection is closing
SOCKADDR_STORAGE addr; // Used for client's remote address
int addrlen; // Length of the address
BUFFER_OBJ *pending, // List of pending buffers to be sent
*pendingtail; // Last entry in buffer list
struct _SOCKET_OBJ *next, // Used to link socket objects together
*prev;
} SOCKET_OBJ;
//
// Allocated for each trhead spawned
//
typedef struct _THREAD_OBJ
{
SOCKET_OBJ *SocketList, // Linked list of all sockets allocated
*SocketListEnd; // End of socket list
int SocketCount; // Number of socket objects in list
HANDLE Event; // Used to signal new clients assigned
// to this thread
HANDLE 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
//
// Statistics counters
//
volatile LONG gBytesRead=0,
gBytesSent=0,
gStartTime=0,
gBytesReadLast=0,
gBytesSentLast=0,
gStartTimeLast=0,
gTotalConnections=0,
gCurrentConnections=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);
}
//
// Function: GetBufferObj
//
// Description:
// Allocate a BUFFER_OBJ. Each receive posted by a receive thread allocates
// one of these. After the recv is successful, the BUFFER_OBJ is queued for
// sending by the send thread. Again, lookaside lists may be used to increase
// performance.
//
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.
//
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). The
// socket objects mantain a list of all buffers received that need to
// be sent.
//
SOCKET_OBJ *GetSocketObj(SOCKET s, int listening)
{
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->listening = listening;
sockobj->addrlen = sizeof(sockobj->addr);
sockobj->event = WSACreateEvent();
if (sockobj->event == NULL)
{
fprintf(stderr, "GetSocketObj: WSACreateEvent failed: %d\n", WSAGetLastError());
ExitProcess(-1);
}
return sockobj;
}
//
// Function: FreeSocketObj
//
// Description:
// Frees a socket object along with any queued buffer objects.
//
void FreeSocketObj(SOCKET_OBJ *obj)
{
BUFFER_OBJ *ptr=NULL,
*tmp=NULL;
ptr = obj->pending;
while (ptr)
{
tmp = ptr;
ptr = ptr->next;
FreeBufferObj(tmp);
}
WSACloseEvent(obj->event);
if (obj->s != INVALID_SOCKET)
{
closesocket(obj->s);
}
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);
}
thread->Event = WSACreateEvent();
if (thread->Event == NULL)
{
fprintf(stderr, "GetThreadObj: WSACreateEvent failed: %d\n", WSAGetLastError());
ExitProcess(-1);
}
thread->Handles[0] = thread->Event;
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: InsertSocketObj
//
// Description:
// Insert a socket object into the list of socket objects for
// the given thread object.
//
int InsertSocketObj(THREAD_OBJ *thread, SOCKET_OBJ *sock)
{
int ret;
EnterCriticalSection(&thread->ThreadCritSec);
if (thread->SocketCount < MAXIMUM_WAIT_OBJECTS-1)
{
sock->next = sock->prev = NULL;
if (thread->SocketList == NULL)
{
// List is empty
thread->SocketList = thread->SocketListEnd = sock;
}
else
{
// Non-empty; insert at the end
sock->prev = thread->SocketListEnd;
thread->SocketListEnd->next = sock;
thread->SocketListEnd = sock;
}
// Assign the socket's event into the thread's event list
thread->Handles[thread->SocketCount + 1] = sock->event;
thread->SocketCount++;
ret = NO_ERROR;
}
else
{
ret = SOCKET_ERROR;
}
LeaveCriticalSection(&thread->ThreadCritSec);
return ret;
}
//
// Function: RemoveSocketObj
//
// Description:
// Remove a socket object from the list of sockets for the given thread.
//
void RemoveSocketObj(THREAD_OBJ *thread, SOCKET_OBJ *sock)
{
EnterCriticalSection(&thread->ThreadCritSec);
if (sock->prev)
{
sock->prev->next = sock->next;
}
if (sock->next)
{
sock->next->prev = sock->prev;
}
if (thread->SocketList == sock)
thread->SocketList = sock->next;
if (thread->SocketListEnd == sock)
thread->SocketListEnd = sock->prev;
thread->SocketCount--;
// Signal thread to rebuild array of events
WSASetEvent(thread->Event);
InterlockedDecrement(&gCurrentConnections);
LeaveCriticalSection(&thread->ThreadCritSec);
}
//
// Function: FindSocketObj
//
// Description:
// Find a socket object within the list of sockets from a thread. The socket
// object is found by index number -- this must be so because the index of
// the event object in the thread's event array must match the order in which
// the socket object appears in the thread's socket list.
//
SOCKET_OBJ *FindSocketObj(THREAD_OBJ *thread, int index)
{
SOCKET_OBJ *ptr=NULL;
int i;
EnterCriticalSection(&thread->ThreadCritSec);
ptr = thread->SocketList;
for(i=0; i < index ;i++)
{
ptr = ptr->next;
}
LeaveCriticalSection(&thread->ThreadCritSec);
return ptr;
}
//
// Function: EnqueueBufferObj
//
// Description:
// Queue up a receive buffer for this connection (socket).
//
void EnqueueBufferObj(SOCKET_OBJ *sock, BUFFER_OBJ *obj, BOOL AtHead)
{
if (sock->pending == NULL)
{
// Queue is empty
sock->pending = sock->pendingtail = obj;
}
else if (AtHead == FALSE)
{
// Put new object at the end
sock->pendingtail->next = obj;
sock->pendingtail = obj;
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -