Valhalla Legends Forums Archive | C/C++ Programming | Winsock Wrapper.

AuthorMessageTime
Win32
Since I noticed a few people have problems with Winsock. You can use a wrapper of mine, if you like.
Keep in mind, it's from another project of mine, and therefor contains slight application-specific functionality.

A few notes:
* Specific error codes can all be retrieved through the Winsock API's GetLastError() function.
* Only asynchronous operation is supported.
* If SocketReceive() detects a graceful connection-closure, 'iCount' will equal SOCKET_ERROR.
* NetworkStartup() and NetworkCleanup() replace WSAStartup() and WSACleanup() respectivly.
* The WSADATA structure typically returned from WSAStartup() is exempted, but you can easily modify that.


[Network.cpp]
[code]
/**
** Network.cpp
** ===========
** Copyright (C) Matthew J. W.
** Version 1.0
**
**
** Description:
**   Contains the implementation of the Network module.
**
**
** Abstract:
**   The Network module provides a core framework for remote networking.
**
**
** Details:
**   The Network module is simplified version of the Winsock(2) API.
**
**   The module was written to, one, provide a cleaner interface for networking,
**   and two to convert Winsock's error scheme to the Window's APIs.
**
**
*******************************************************************************************************************************************************/
#include "Types.h"
#include "Network.h"




////////////////////////////////////////////////////////////////////////////
//
// Functions
//
////////////////////////////////////////////////////////////////////////////

Bool __stdcall NetworkStartup()
{
WSADATA      WinsockInfo;
register Int iResult;


//
// Load the Winsock library.
//
iResult = WSAStartup(MAKEWORD(2, 2), &WinsockInfo);

if(!iResult)
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(iResult);
}


//
// FAILURE:
//
return FALSE;
}



Bool __stdcall NetworkCleanup()
{
//
// Unload the Winsock library.
//
if(!WSACleanup())
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// FAILURE:
//
return FALSE;
}



////////////////////////////////////////////////////////////////////////////
//
// Hostname Functions
//
////////////////////////////////////////////////////////////////////////////

Bool __fastcall ResolveHostname(const Char* pstrHostname, ADDRINFO*& rpHostInfo)
{
register Int iResult;


//
// Resolve the given hostname.
//
iResult = getaddrinfo(pstrHostname, NULL, NULL, &rpHostInfo);

if(!iResult)
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(iResult);
}


//
// FAILURE:
//
return FALSE;
}



Void __stdcall FreeHostInfo(ADDRINFO*& rpHostInfo)
{
//
// Free the hostname information.
//
freeaddrinfo(rpHostInfo);
}



////////////////////////////////////////////////////////////////////////////
//
// Socket Functions
//
////////////////////////////////////////////////////////////////////////////

Bool __stdcall CreateSocket(SOCKET& rhSocket, Int iType, Int iTransportProtocol)
{
//
// Create a new socket, explicitly implementing the TCP/IP protocol family,
// along with the desired network/transport protocols.
//
rhSocket = socket(AF_INET, iType, iTransportProtocol);

if(rhSocket != INVALID_SOCKET)
{
Dword dwAsync = TRUE;


/*
* All sockets which are involved with the Network module are non-blocking.
*
*/
if(!ioctlsocket(rhSocket, FIONBIO, (u_long*) &dwAsync))
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// ERROR CLEANUP:
//
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// FAILURE:
//
return FALSE;
}



Bool __stdcall SocketBind(SOCKET& rhSocket, const SOCKADDR_IN& rAddress)
{
//
// Associate the socket with the desired address.
//
if(!bind(rhSocket, (SOCKADDR*) &rAddress, sizeof(SOCKADDR_IN)))
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// FAILURE:
//
return FALSE;
}



Bool __stdcall SocketConnect(SOCKET& rhSocket, const SOCKADDR_IN& rHostAddress)
{
register Int iResult;


//
// Initiate a connection with the host.
//
connect(rhSocket, (const SOCKADDR*) &rHostAddress, sizeof(SOCKADDR_IN));
iResult = WSAGetLastError();


/*
* Since we're using non-blocking sockets, a connection can never be established
* immediately and 'connect' will always fail with WSAEWOULDBLOCK.
*
*/
if(iResult == WSAEWOULDBLOCK)
{
/*
* Although it's not typically a good idea, for performance reasons, we're going
* to use 'select' to determine when a connection has been established (Which is
* indicated when the socket is writeable).
*
* But since connection-establishment is not very common (Compared to reads & writes),
* there's not going to be much of an overall performance overhead.
*
*
* NOTE:
*   There is a possiblity that we're going to get stuck in an infinite-loop. Why?
*   Because the following loop which waits for a connection to be established will
*   only break when either a connection is established, or an error occurs. But it's
*   possible neither of these conditions will never even happen.
*
*   But from experience, this has never happened. But it's a possibility that needs
*   to be noted.
*
*/
fd_set  Status;
TIMEVAL Timeout;

*Status.fd_array = rhSocket;
Status.fd_count  = 1;
Timeout.tv_sec   = 0;
Timeout.tv_usec  = 0;


//
// Wait for the connection to become properly established.
//
for(;;)
{
//
// Check if the connection-attempt has completed successfully.
//
if(select(NULL, NULL, &Status, NULL, (const TIMEVAL*) &Timeout) == 1)
{
//
// SUCCESS:
//
return TRUE;
}

Status.fd_count++;


//
// Check if the connection-attempt failed due to an error.
//
if(select(NULL, NULL, NULL, &Status, (const TIMEVAL*) &Timeout) == 1)
{
Int iErrorLen = sizeof(Int);


/*
* A further exception to our neat&tidy error-handling scheme.
*
* Unlike other Winsock calls where we could just use WSAGetLastError(),
* we must now use getsockopt() to retrieve the error-code identifying
* why the connection-attempt failed.
*
* This complicates matters because getsockopt() can also fail (Even though
* from recilection, never has, but it's a possibility!).
*
* So what happens if getsockopt() does fail? We're going to return the
* error-code it returns, because it's all we really got.
*
* If ambiguous errors occur from SocketConnect(), stack-tracing should
* be enabled.
*
* One more thing to note, 'iResult' is going to be used to store the error-
* code returned by getsockopt().
*
*/
if(!getsockopt(rhSocket, SOL_SOCKET, SO_ERROR, (char*) &iResult, (int*) &iErrorLen))
{
//
// ERROR:
//
SetLastError(iResult);
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}

break;
}

Status.fd_count++;


//
// Ensure we don't hog the CPU.
//
Sleep(1);
}
}

else
{
//
// ERROR:
//
SetLastError(iResult);
}


//
// FAILURE:
//
return FALSE;
}



Bool __stdcall SocketSend(SOCKET& rhSocket, const Void* pBuffer, Int iBytes, Int& riCount, Int iFlags)
{
//
// Send the contents of the given buffer to our peer.
//
riCount = send(rhSocket, (const char*) pBuffer, iBytes, iFlags);

if(riCount != SOCKET_ERROR)
{
//
// SUCCESS:
//
return TRUE;
}

else
{
riCount = WSAGetLastError(); // Re-using 'riCount' to store the error-code.


/*
* Unlike Winsock, there being zero buffer-space on the TCP window available
* is not going to be treated as an erroneous condition. Instead, we're
* just going to inform the user that no data was able to be sent.
*
*/
if(riCount == WSAEWOULDBLOCK)
{
riCount = 0; // Zero bytes sent.


//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(riCount);
}
}


//
// FAILURE:
//
return FALSE;
}



Bool __stdcall SocketReceive(SOCKET& rhSocket, Void* pBuffer, Int iBytes, Int& riCount, Int iFlags)
{
//
// Receive the desired number of bytes our peer has sent us.
//
riCount = send(rhSocket, (char*) pBuffer, iBytes, iFlags);

if(riCount != SOCKET_ERROR)
{
/*
* There's a possibility the connection was closed gracefully by our peer,
* this will be indicated if zero bytes were received.
* Due to the way SocketReceive() is defined, we will have to set the
* received-bytes count to SOCKET_ERROR to indicate to the caller
* that the connection was closed.
*
*/
if(!riCount) riCount = SOCKET_ERROR;


//
// SUCCESS:
//
return TRUE;
}

else
{
riCount  = WSAGetLastError(); // Re-using 'riCount' to save stack push.


/*
* Again, unlike Winsock, if no data has arrived yet from our peer, instead
* of treating it as an error we'll just let the user know that no data
* was able to be copied into their buffer.
*
*/
if(riCount == WSAEWOULDBLOCK)
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(riCount);
}
}


//
// FAILURE:
//
return FALSE;
}



Bool __fastcall SocketShutdown(SOCKET& rhSocket)
{
//
// Disable I/O capabilities of the socket.
//
if(!shutdown(rhSocket, SD_BOTH))
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// FAILURE:
//
return FALSE;
}



Bool __fastcall DestroySocket(SOCKET& rhSocket)
{
//
// Destroy the socket.
//
if(!closesocket(rhSocket))
{
//
// SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
SetLastError(WSAGetLastError());
}


//
// FAILURE:
//
return FALSE;
}
[/code]

[Network.h]
[code]
/**
** Network.h
** =========
** Copyright (C) Matthew J. W.
** Version 1.0
**
**
** Description:
**   Interface to the Network module.
**
**
*******************************************************************************************************************************************************/
#pragma once




////////////////////////////////////////////////////////////////////////////
//
// System Dependencies
//
////////////////////////////////////////////////////////////////////////////
#include <winsock2.h> // Winsock API.
#include <ws2tcpip.h> // Winsock TCP/IP extensions.
#include <windows.h>  // Windows API.



////////////////////////////////////////////////////////////////////////////
//
// Socket Types
//
////////////////////////////////////////////////////////////////////////////
#define SOCKET_TYPE_STREAM   SOCK_STREAM
#define SOCKET_TYPE_MESSAGE  SOCK_DGRAM



////////////////////////////////////////////////////////////////////////////
//
// Transport Protocols
//
////////////////////////////////////////////////////////////////////////////
#define TRANSPORT_PROTOCOL_TCP  IPPROTO_TCP
#define TRANSPORT_PROTOCOL_UDP  IPPROTO_UDP



////////////////////////////////////////////////////////////////////////////
//
// Functions
//
////////////////////////////////////////////////////////////////////////////
extern Bool __stdcall  NetworkStartup();
extern Bool __stdcall  NetworkCleanup();
extern Bool __fastcall ResolveHostname(const Char* pstrHostname, ADDRINFO*& rpHostInfo);
extern Void __stdcall  FreeHostInfo(ADDRINFO*& rpHostInfo);
extern Bool __stdcall  CreateSocket(SOCKET& rhSocket, Int iType, Int iTransportProtocol);
extern Bool __stdcall  SocketBind(SOCKET& rhSocket, const SOCKADDR_IN& rAddress);
extern Bool __stdcall  SocketConnect(SOCKET& rhSocket, const SOCKADDR_IN& rHostAddress);
extern Bool __stdcall  SocketSend(SOCKET& rhSocket, const Void* pBuffer, Int iBytes, Int& riCount, Int iFlags = NULL);
extern Bool __stdcall  SocketReceive(SOCKET& rhSocket, Void* pBuffer, Int iBytes, Int& riCount, Int iFlags = NULL);
extern Bool __fastcall SocketShutdown(SOCKET& rhSocket);
extern Bool __fastcall DestroySocket(SOCKET& rhSocket);
[/code]

[Types.h]
[code]
/**
** Types.h
** =======
** Copyright (C) Matthew J. W.
** All rights reserved.
** Version 1.0
**
**
** Description:
**   This file contains the declaration of commonly used, primitive, data-types.
**
**
*******************************************************************************************************************************************************/
#pragma once



////////////////////////////////////////////////////////////////////////////
//
// Fundamental Types
//
////////////////////////////////////////////////////////////////////////////
typedef void                Void;
typedef char                Char;
typedef unsigned long int   Bool;
typedef unsigned char       Byte;
typedef unsigned short      Word;
typedef unsigned long int   Dword;
typedef unsigned long long  Qword;
typedef signed short        Short;
typedef signed long int     Int;
typedef signed long long    Long;
[/code]


-Matt
September 2, 2006, 7:37 PM

Search