Tweets

Generic Network Server

This code implements a generic TCP/IP listener. I have derived from this class to produce multithreaded servers.

This interface assumes that the user is interested in working at the Winsock level of detail. If I were going to transform this code to be cross-platform or for use by those with no experience with Winsock then I would abstract and hide the Window's specifics (e.g., SOCKET and other Winsock-specific structures and functions).

Note that this is a console application [uses fprintf(stderr,...)] - the message mechanism could be easily abstracted but doing so would have violated the goal of simplicity, until such time as that abstraction is necessary.

Compatibility/System Requirements

This code was compiled with VC7 on Windows 2000 and Windows XP systems.

Code

// Servertools
// Copyright (c) 2004, 2005 Robin Eric Fredericksen
// -- All rights reserved.
#include <WinSock2.h>


//==================================================
// The Interface
//==================================================
class CServer
{

public
:

enum

{


DEFAULT_LISTEN_IP = INADDR_ANY, // all interfaces
DEFAULT_LISTEN_PORT = 10000,

DEFAULT_SOCKET_TIMEOUT_ms = 8000,
DEFAULT_CONNECTION_QUEUE = 5
};


protected
:
bool
m_bWSAInitialized;
bool
m_bSocketInitialized;
WSADATA m_oWSAData;

void
SetListenAddress(DWORD _dwHBO_IP, USHORT _HBO_Port);
sockaddr_in m_oListenAddress;

SOCKET m_sListenSocket;
bool
InitWSA(void);
void
PrintWSALastError(void);

public
:

CServer(void);
virtual
~CServer();

bool
InitSuccess(void) { return( m_bWSAInitialized &&
m_bSocketInitialized ); }

// return default interface IP in host byte order
DWORD DefaultIP(void);
const
char * DefaultDottedIP(void);

const
char * DottedIP(DWORD _HBO_IP);

// parms in host byte order
bool BindTo( DWORD _dwHBO_IP, USHORT _HBO_Port,
USHORT _usMaxWaitingConnections=DEFAULT_CONNECTION_QUEUE

);


// never returns! call from thread that knows this
// calls DispatchConnection upon successful accept() call
void StartListening(void);

// override this to do the work in your derived class

virtual void DispatchConnection(SOCKET _sWorkSocket);

};



//==================================================
// The implementation
//==================================================

//==================================================
virtual void DispatchConnection(SOCKET _sWorkSocket)
{


// just say no
shutdown(_sWorkSocket, SD_BOTH);
Sleep(1000);

closesocket(_sWorkSocket);
}


//==================================================
bool CServer::InitWSA(void)
{


return
( 0 == WSAStartup(MAKEWORD(2, 0), &m_oWSAData) );
}


//==================================================
void CServer::SetListenAddress(DWORD _dwHBO_IP, USHORT _HBO_Port)
{

m_oListenAddress.sin_family = AF_INET;

m_oListenAddress.sin_addr.s_addr = htonl(_dwHBO_IP);
m_oListenAddress.sin_port = htons(_HBO_Port);
}


//==================================================
CServer::CServer(void)
{

m_bWSAInitialized = InitWSA();

m_bSocketInitialized = false;
SetListenAddress(DEFAULT_LISTEN_IP, DEFAULT_LISTEN_PORT);
}


//==================================================
CServer::~CServer()
{

if
( m_bWSAInitialized ) WSACleanup();
}


//==================================================
const char * CServer::DefaultDottedIP(void)
{

DWORD dwIP = DefaultIP();

return
( DottedIP( dwIP ) );
}


//==================================================
const char * CServer::DottedIP(DWORD _HBO_IP)
{


DWORD dwIP = htonl(_HBO_IP);
return
( inet_ntoa( *(in_addr*)&dwIP ) );
}


//==================================================
DWORD CServer::DefaultIP(void)
{


DWORD dwIP = INADDR_NONE;

if
( m_bWSAInitialized )
{

hostent * poHostName = gethostbyname(NULL);

if
( poHostName )
{

dwIP = *((DWORD*)poHostName->h_addr);
}
}


return
( ntohl(dwIP) );
}


//==================================================
void CServer::PrintWSALastError(void)
{


DWORD dwError = WSAGetLastError();
fprintf(stderr, _T("!!WSALastError(%u:%X:%d)\n"),
dwError,dwError,dwError );
}


//==================================================
bool CServer::BindTo(DWORD _dwHBO_IP, USHORT _HBO_Port,
USHORT _usMaxWaitingConnections)
{


if
( !m_bWSAInitialized ) return( false );

SetListenAddress(_dwHBO_IP, _HBO_Port);

// do all the work of setting up the listening socket

m_sListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if
( INVALID_SOCKET == m_sListenSocket )
{

// record the error here for later inspection
fprintf(stderr, _T("!!Could not create socket\n") );

PrintWSALastError();
return
( false );
}


// do this so that we don't run out of source ports...
BOOL bReuse = TRUE;

int
iResult = setsockopt(m_sListenSocket,
SOL_SOCKET, SO_REUSEADDR,
(
LPCSTR)&bReuse, sizeof(bReuse) );

if
( SOCKET_ERROR == iResult )
{

// provide feedback/warning on failure for later inspection
fprintf(stderr, _T("Could not setsockopt(SO_REUSEADDR)\n") );

PrintWSALastError();
// not a critical error
}

// make sure the timeouts are what we want
DWORD dwTimeout_ms = DEFAULT_SOCKET_TIMEOUT_ms;

iResult = setsockopt(m_sListenSocket, SOL_SOCKET, SO_RCVTIMEO,
(
const char*)&dwTimeout_ms, sizeof(dwTimeout_ms) );

if
( SOCKET_ERROR == iResult )
{

// send warning message
fprintf(stderr, _T("Could not setsockopt(SO_RCVTIMEO, %d)\n"),
dwTimeout_ms );

PrintWSALastError();
// not a critical error
}

iResult = setsockopt(m_sListenSocket, SOL_SOCKET, SO_SNDTIMEO,
(
const char*)&dwTimeout_ms, sizeof(dwTimeout_ms) );

if
( SOCKET_ERROR == iResult )
{

// send warning message
fprintf(stderr, _T("Could not setsockopt(SO_SNDTIMEO, %d)\n"),
dwTimeout_ms );

PrintWSALastError();
// not a critical error
}

iResult = bind(m_sListenSocket, (sockaddr*)&m_oListenAddress,
sizeof
(m_oListenAddress) );

if
( SOCKET_ERROR == iResult )
{

// send warning message
fprintf(stderr, _T("!!Could not bind(%s:%u)\n"),
DottedIP(_dwHBO_IP),
_HBO_Port);

PrintWSALastError();
// critical error
return(false);
}


iResult = listen(m_sListenSocket, _usMaxWaitingConnections );

if
( SOCKET_ERROR == iResult )
{

// send warning message
fprintf(stderr, _T("!!Could not listen(%u:%u)\n"), m_sListenSocket,
_usMaxWaitingConnections);

PrintWSALastError();
// critical error
return(false);
}


m_bSocketInitialized = true;

fprintf(stderr, _T("Server listening on (%s:%u) with queue length %d\n"),
DottedIP(DefaultIP()),
_HBO_Port,

_usMaxWaitingConnections
);


return
( m_bSocketInitialized );
}



//==================================================
void CServer::StartListening(void)
{

if
( !m_bSocketInitialized )
{


fprintf(stderr,_T("Listening socket not yet initialized\n") );
return
;
}


while
(TRUE)
{


// this does not time out because the socket is blocking.
SOCKET sNewSocket = accept(m_sListenSocket, NULL, NULL);

if
( SOCKET_ERROR == sNewSocket )
{

// record error message
fprintf(stderr,_T("Error on accept()\n") );

PrintWSALastError();
break
; // quit from the loop and exit the server
}
// hand off the connection
DispatchConnection( sNewSocket );
}
}