Author | Message | Time |
---|---|---|
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 |