This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Multi-user Network Class Submitted by

Here is an unpolished and unfinished TCP/IP & UDP Networking Manager. This is the initial base code for an article I was (well, am) willing to write about multi-user networking. It is a non-blocking, window-based multi-user client-server code with user-defined callbacks. It is easy to use, one extension is available as a simple http download class. Basically I'm publishing it to hear some criticisms. Added to my bad coding style is the absolutely unfinished UDP Manager.
Description from readme.txt:
"This is a technical, OOP C++ programming sample code. You will need some previous C++ programming knowledge to fully understand it. This networking class provides non-blocking, easy to use TCP/IP and UDP client-server objects for networked applications. This is the same code that I use on my networked applications. There is an HTTP class that can do multiple file downloads as well. This code may be updated from time to time. You are free to use this code on anything you desire, as long as you do not state that you wrote the class for yourself. A mention is appreciated if you use this code solution on your own projects."
Regards,
Vander Nunes
http://www.virtware.net

Currently browsing [vnnetclass.zip] (17,401 bytes) - [net.h] - (2,057 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    // ------------------------------------------------------------------------------------------
    // applications using net classes should include netserver.h and/or netclient.h
    // ------------------------------------------------------------------------------------------

    #ifndef _NET_H_

      #define _NET_H_
      #include <windows.h>

      // include wsock32.lib in the link
      #pragma comment (lib, "wsock32.lib")

      // type of socket
      enum enSockType { stUDP, stTCP };

      // a network packet
      typedef struct _NETPACKET
      {
        void   *pBuffer;
        int    iLength;
      } NETPACKET;

      // ----------------------------------------------------------------------------------------------
      // Net Events - most will be repassed to the user's callback
      // ----------------------------------------------------------------------------------------------

      // internal use
      #define NET_SELECT        WM_USER+10

      // internal use by resolver
      #define NET_RESOLVE       WM_USER+11

      // new incoming connection
      #define NET_NEWCONNECTION WM_USER+20

      // new outcoming connection
      #define NET_CONNECTED     WM_USER+21

      // new data waiting
      #define NET_NEWDATA       WM_USER+22

      // client disconnection
      #define NET_DISCONNECTION WM_USER+23

      // refused incoming connection
      #define NET_REFCONNECTION WM_USER+24

      // ----------------------------------------------------------------------------------------------

      // ------------------------------------------------------------------------------------------
      // this is a private function used by CNetServer and CNetClient classes.
      // ------------------------------------------------------------------------------------------

      HWND NetMakeWindow(const char *szTitle, DWORD dwRefCount, int iWidth, int iHeight, DWORD dwFlags, WNDPROC hWndProc);

    #endif

Currently browsing [vnnetclass.zip] (17,401 bytes) - [http.h] - (4,207 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #ifndef _HTTP_H_

      #define _HTTP_H_
      #include <windows.h>
      #include <stdio.h>

      // size of socket buffer
      #define BUFFER_SIZE 1024

      class CNetClient;

      // download stages enumeration
      enum enHttpDownStage { dsIdle, dsConn, dsReq, dsRecv, dsFail };

      // http get file request
    //  #define HTTP_GETREQUEST "GET %s%s%s HTTP/1.1\nUser-Agent: VirtwareA1\nHost: %s\nConnection: close\r\n\r\n"
      #define HTTP_GETREQUEST "GET %s%s%s HTTP/1.1\nHost: %s\nConnection: close\n\n"

      // request OK, file found
      #define HTTP_GETOK      "200 OK"

      // request OK, file found but in another place
      // (must parse response and build the request again)
      // ### not used yet
      #define HTTP_GETOKREDIR "302 Found"

      // end of the http header
      #define HTTP_HEADEREND "\r\n\r\n"

      // user's callback
      #define USR_HTTPCALLBACK(userProc) void (*userProc)(CHttp *pHttp, void *pUserData, enHttpDownStage Stage, DWORD dwBytesDownloaded)

      // ----------------------------------------------------------------------------------------------
      //
      // CHttp download class
      //
      // ----------------------------------------------------------------------------------------------
      class CHttp
      {
        friend void HttpClientCallback(CNetClient *pClient, void *pData, int iMsg);
        friend LRESULT CALLBACK HttpWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

        private:

          // download stage
          enHttpDownStage           m_Stage;

          // hidden window to handle timer timeout
          HWND                      m_hWnd;

          // flag if a download is in progress
          BOOL                      m_bWorking;

          // socket
          CNetClient               *m_pClient;

          // host, file and target
          char                      m_szHost[MAX_PATH];
          char                      m_szFile[MAX_PATH];
          char                      m_szTarget[MAX_PATH];

          // proxy info
          BOOL                      m_bUseProxy;
          char                      m_szProxyAddr[33];
          int                       m_iProxyPort;

          // file
          FILE                     *m_hFile;
          DWORD                     m_dwBytesDownloaded;

          // user callback
          USR_HTTPCALLBACK(m_userProc);
          void                     *m_pUserData;

          // internal callback
          void                      Callback(CNetClient *pClient, int iMsg);

          // internal search for string
          int CHttp::strpos(char *szStr, char *szString);

          void                      CloseDownload();
          void                      Fail();

        public:

          CHttp(USR_HTTPCALLBACK(userProc), void *pUserData);
          virtual ~CHttp();

          // ------------------------------------------------------------------------------------------
          // download a file from a url
          // ------------------------------------------------------------------------------------------

          BOOL                      Download(char *szHost, char *szFile, char *szTarget, int iPort=80, char *szProxyAddr=NULL, int iProxyPort=0);

          // ------------------------------------------------------------------------------------------
          // stop a download
          // ------------------------------------------------------------------------------------------

          void                      Stop();

          // ------------------------------------------------------------------------------------------
          // return the actual download stage
          // ------------------------------------------------------------------------------------------

          enHttpDownStage           Stage() { return m_Stage; }

          // ------------------------------------------------------------------------------------------
          // tell if a download is in progress
          // ------------------------------------------------------------------------------------------

          BOOL                      Working() { return m_bWorking; }
      };

    #endif

Currently browsing [vnnetclass.zip] (17,401 bytes) - [net.cpp] - (2,493 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #include "net.h"
    #include <stdio.h>

    // ------------------------------------------------------------------------
    // A Basic, still smart window creation function.
    // ------------------------------------------------------------------------
    HWND NetMakeWindow(const char *szTitle, DWORD dwRefCount, int iWidth, int iHeight, DWORD dwFlags, WNDPROC hWndProc)
    {

      if (dwRefCount == 1)
      {
        // our window class
        WNDCLASS wndWc;

        // ---------------------------------------------------------
        // fill window class members
        // ---------------------------------------------------------
        wndWc.style = CS_OWNDC;
        wndWc.lpfnWndProc = (WNDPROC)hWndProc;
        wndWc.cbClsExtra = 0;
        wndWc.cbWndExtra = 0;
        wndWc.hInstance = GetModuleHandle(NULL);
        wndWc.hIcon = NULL;
        wndWc.hCursor = LoadCursor(0, IDC_ARROW);
        wndWc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndWc.lpszMenuName = NULL;
        wndWc.lpszClassName = szTitle;

        // register class
        //if (!RegisterClass(&wndWc)) return false;

        // ### was creating problems with
        // CNetClient and CNetServer,
        // like, the user deleted the first instance of NetClient,
        // then newed[] it again... the reference count was 1 again then,
        // but the class continued registered. To solve this,
        // those classes must delete the window class when RefCount reachs zero.
        // For now, this quick hack will do =)
        RegisterClass(&wndWc);
      }
      // ---------------------------------------------------------

      // get actual screen resolution
      int iSw = (WORD)GetSystemMetrics(SM_CXSCREEN);       // width
      int iSh = (WORD)GetSystemMetrics(SM_CYSCREEN);       // height

      // make a rectangle on the center of the screen
      RECT rc = { (iSw - iWidth)/2, (iSh - iHeight)/2, iWidth, iHeight };

      // create the window. the spaces on the window title
      // are just to make sure this will be visible when the region
      // is active. just run the app and you'll understand. =)
      HWND hWnd = CreateWindow(szTitle, szTitle,
                               WS_OVERLAPPEDWINDOW,
                               rc.left,rc.top, iWidth,iHeight,
                               NULL, NULL, GetModuleHandle(NULL), NULL);

      // return result
      return hWnd;
    }

Currently browsing [vnnetclass.zip] (17,401 bytes) - [http.cpp] - (11,141 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #include "http.h"
    #include "netclient.h"

    // ------------------------------------------------------------------------------------------------
    //
    // Http Window Procedure
    //
    // ------------------------------------------------------------------------------------------------
    LRESULT CALLBACK HttpWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
    {

      // get the pointer to the netserver class
      CHttp *pHttp = (CHttp*)GetProp(hWnd, "http");
      if (!pHttp)
        return DefWindowProc(hWnd,uMessage,wParam,lParam);

      // handle the message
      switch (uMessage)
      {

        case WM_TIMER:
        {
          // connection timed out.
          if (pHttp->m_Stage == dsConn || pHttp->m_Stage == dsReq)
            pHttp->Fail();

          break;
        }

      }

      // return default window procedure result
      return DefWindowProc(hWnd,uMessage,wParam,lParam);
    }

    // ----------------------------------------------------------------------------
    //
    // Private callback for the socket client - just pass to the class callback
    //
    // ----------------------------------------------------------------------------
    void HttpClientCallback(CNetClient *pClient, void *pData, int iMsg)
    {
      CHttp *pHttp = (CHttp*)pData;
      pHttp->Callback(pClient, iMsg);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Constructor
    //
    // User instantiating the class can provide a callback to receive http events,
    // and store a pointer to any data wanted. This data will be repassed to the callback.
    //
    // ------------------------------------------------------------------------------------------------
    CHttp::CHttp(USR_HTTPCALLBACK(userProc), void *pUserData)
    {
      m_hFile = NULL;
      m_pClient = NULL;
      m_Stage = dsIdle;
      m_dwBytesDownloaded = 0;
      m_userProc = userProc;
      m_pUserData = pUserData;
      m_bWorking = FALSE;

      m_bUseProxy = FALSE;
      m_szProxyAddr[0] = 0;
      m_iProxyPort = 0;

      m_hWnd = NetMakeWindow("http",1,64,64,WS_OVERLAPPEDWINDOW,HttpWndProc);
      if (!m_hWnd) throw (1);

      SetProp(m_hWnd,"http",this);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Destructor - closes any pending download before instance destruction.
    //
    // ------------------------------------------------------------------------------------------------
    CHttp::~CHttp()
    {
      CloseDownload();
      if (m_hWnd) DestroyWindow(m_hWnd);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Start a download.
    // If you need multiple downloads, use multiple instances of the class.
    //
    // ex: pHttp->Download("www.virtware.net", "/downs/techdemo.exe", "c:\\downs\\techdemo.exe");
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CHttp::Download(char *szHost, char *szFile, char *szTarget, int iPort, char *szProxyAddr, int iProxyPort)
    {
      // refuses to start a new download if a download is happening.
      if (m_bWorking) return FALSE;

      // safe check names
      if (strlen(szHost)>=MAX_PATH || strlen(szFile)>=MAX_PATH || strlen(szTarget)>=MAX_PATH)
        return FALSE;

      if (szProxyAddr)
      {
        if (strlen(szProxyAddr) > sizeof(m_szProxyAddr))
          return FALSE;
      }

      // check if there is proxy information
      if (szProxyAddr && iProxyPort)
      {
        strcpy(m_szProxyAddr, szProxyAddr);
        m_iProxyPort = iProxyPort;
        m_bUseProxy = TRUE;
      }
      else
        m_bUseProxy = FALSE;

      try
      {
        m_pClient = new CNetClient(stTCP, HttpClientCallback, this, BUFFER_SIZE);

        if (!m_bUseProxy)
          m_pClient->Connect(szHost, iPort);
        else
          m_pClient->Connect(szProxyAddr, iProxyPort);
      }
      catch (...)
      {
        // failed to initialize CNetClient
        // (maybe because already downloading something?)
        return FALSE;
      }

      m_Stage = dsConn;
      strcpy(m_szHost, szHost);
      strcpy(m_szFile, szFile);
      strcpy(m_szTarget, szTarget);
      m_dwBytesDownloaded = 0;

      m_bWorking = TRUE;

      // callback user function
      if (m_userProc)
        m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);

      // set a timer
      // to timeout the connection+request stages.
      SetTimer(m_hWnd, 1, 60000, NULL);

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Private http callback - handles socket connection and data receiving
    //
    // ------------------------------------------------------------------------------------------------
    void CHttp::Callback(CNetClient *pClient, int iMsg)
    {
      char szTmp[512];

      switch (iMsg)
      {

        case NET_CONNECTED:
        {
          // send GET request to server
          if (!m_bUseProxy)
            sprintf(szTmp, HTTP_GETREQUEST, "", "", m_szFile, m_szHost);
          else
            sprintf(szTmp, HTTP_GETREQUEST, "http://", m_szHost, m_szFile, m_szHost);

          pClient->Send((void*)szTmp,strlen(szTmp)+1);

          // change stage
          m_Stage = dsReq;

          // callback user function
          if (m_userProc)
            m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);

          break;
        }

        case NET_NEWDATA:
        {

          NETPACKET *pPacket = pClient->Read();

          switch (m_Stage)
          {

            // ----------------------------------------------------------------------------------------
            // Requesting
            // ----------------------------------------------------------------------------------------
            case dsReq:
            {

              // kill the time out timer
              KillTimer(m_hWnd, 1);

              // expecting server response to GET request
              if ( strpos(HTTP_GETOK, (char*)pPacket->pBuffer) >= 0 )
              {
                // open the target file
                m_hFile = fopen(m_szTarget, "wb+");
                if (!m_hFile)
                {
                  // failed to open target file
                  Fail();
                  return;
                }

                // find the end of the answer header
                // (the start of the file can be glued at the end of the header)
                int iFirst = strpos(HTTP_HEADEREND, (char*)pPacket->pBuffer);

                // if found the end, see if there is something to write already
                if (iFirst >= 0)
                {
                  // skip the header end (will point at the first byte of the file or end of the block)
                  iFirst += strlen(HTTP_HEADEREND);

                  // check to see if there is data remaining to write yet
                  if (pPacket->iLength > iFirst)
                  {
                    // there is file data already, write it
                    m_dwBytesDownloaded = pPacket->iLength-iFirst;
                    BYTE *p = (BYTE*)pPacket->pBuffer;
                    fwrite(&p[iFirst], 1, m_dwBytesDownloaded, m_hFile);
                  }
                }

                // change stage
                m_Stage = dsRecv;

                // callback user function
                if (m_userProc)
                  m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);
              }
              else
              {
                if (pPacket->iLength < BUFFER_SIZE)
                {
                  // failed
                  Fail();
                }
              }

              break;
            }
            // ----------------------------------------------------------------------------------------

            // ----------------------------------------------------------------------------------------
            // Downloading
            // ----------------------------------------------------------------------------------------
            case dsRecv:
            {

              do
              {

                if (pPacket->iLength > 0)
                {
                  // write to file
                  fwrite(pPacket->pBuffer, 1, pPacket->iLength, m_hFile);

                  // update bytes downloaded
                  m_dwBytesDownloaded += pPacket->iLength;

                  // callback user function
                  if (m_userProc)
                    m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);
                }

                // get new packet
                pPacket = pClient->Read();

              } while (pPacket->iLength > 0);

              // check if there was an error code
              if (pPacket->iLength == SOCKET_ERROR)
              {
                int nRc = WSAGetLastError();

                // check why we received an error code
                if (nRc != WSAEWOULDBLOCK)
                {
                  // finished - ### guess if this happens here, we failed to download?
                  // Fail();
                  Stop();
                }
              }

              break;

            }
            // ----------------------------------------------------------------------------------------

          }

          break;
        }

        case NET_DISCONNECTION:
        {
          // finished
          Stop();

          break;
        }

      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Just close any pending download - doesn't change stage nor call user.
    //
    // ------------------------------------------------------------------------------------------------
    void CHttp::CloseDownload()
    {
      if (m_hFile)
      {
        fclose(m_hFile);
        m_hFile = NULL;
      }

      if (m_pClient)
      {
        delete m_pClient;
        m_pClient = NULL;
      }

      m_bWorking = FALSE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Stop a download (with success)
    //
    // ------------------------------------------------------------------------------------------------
    void CHttp::Stop()
    {
      CloseDownload();

      m_Stage = dsIdle;

      // callback user function
      if (m_userProc)
        m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Stop a download (with fail)
    //
    // ------------------------------------------------------------------------------------------------
    void CHttp::Fail()
    {
      CloseDownload();

      m_Stage = dsFail;

      // callback user function
      if (m_userProc)
        m_userProc(this, m_pUserData, m_Stage, m_dwBytesDownloaded);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Private helper method - tell where inside szString is located szStr
    //
    // ------------------------------------------------------------------------------------------------
    int CHttp::strpos(char *szStr, char *szString)
    {
      DWORD p = 0;

      for (DWORD i=0; i<strlen(szString); i++)
      {
        if (szString[i] == szStr[p])
        {
          p++;
          if (p >= strlen(szStr))
            return i-(p-1);
        }
        else p = 0;
      }

      return -1;
    }

Currently browsing [vnnetclass.zip] (17,401 bytes) - [netclient.cpp] - (12,291 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #include <windows.h>
    #include "netclient.h"

    DWORD CNetClient::m_dwRefCount = 0;

    // ------------------------------------------------------------------------------------------------
    //
    // Client Window Procedure
    //
    // ------------------------------------------------------------------------------------------------
    LRESULT CALLBACK NetClientWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
    {

      // get the pointer to the netserver class
      CNetClient *pClient = (CNetClient*)GetProp(hWnd, "netclient");
      if (!pClient)
        return DefWindowProc(hWnd,uMessage,wParam,lParam);

      // handle the message
      switch (uMessage)
      {

        case NET_SELECT:
        {
          pClient->Select(wParam, lParam);
          break;
        }

        case NET_RESOLVE:
        {
          if ((HANDLE)wParam == pClient->m_hResolver)
          {
            // this is our hostbyname result.
            int nRc = HIWORD(lParam);

            switch (nRc)
            {
              case 0:
              {
                // successful
                struct hostent *phe = (struct hostent *)pClient->m_szHostData;
                pClient->ContinueConnect(phe);
                break;
              }

              case WSAHOST_NOT_FOUND:
              {
                // authoritative: host not found
                break;
              }

              case WSATRY_AGAIN:
              {
                // non-authoritative host not found,
                // or server-fail.
                break;
              }

              default:
              {
                // there are more possible errors,
                // but we'll treat then as default.
                break;
              }

            }

          }
          break;
        }

      }

      // return default window procedure result
      return DefWindowProc(hWnd,uMessage,wParam,lParam);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Construction
    //
    // ------------------------------------------------------------------------------------------------
    CNetClient::CNetClient(enSockType SockType, USR_CLNTCALLBACK(userProc), void *pUserData, int iBufferSize)
    {
      m_bInitialized = FALSE;
      m_dwRefCount++;

      // try to initialize buffers
      if ( !Init(SockType, userProc, pUserData, iBufferSize) ) throw (0);

      // try to create hidden window
      if ( !(m_hWnd = NetMakeWindow("netclient",m_dwRefCount,64,32,WS_OVERLAPPEDWINDOW,NetClientWndProc)) ) throw (1);

      // put our address in the window properties
      if ( !SetProp(m_hWnd, "netclient", (HANDLE)this) ) throw (2);

      m_bInitialized = TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Initialization
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetClient::Init(enSockType SockType, USR_CLNTCALLBACK(userProc), void *pUserData, int iBufferSize)
    {
      if (m_dwRefCount == 1)
      {
        // start winsock
        WSADATA wsaData;
        WORD wVersionRequested = MAKEWORD(1,1);
        WSAStartup(wVersionRequested, &wsaData);
      }

      // clear socket
      m_Sock = INVALID_SOCKET;

      // store connection information
      m_SockType = SockType;
      m_userProc = userProc;
      m_pUserData = pUserData;

      // initialize buffer
      m_iRecvBufferSize = iBufferSize;
      m_pRecvBuffer = new char[m_iRecvBufferSize];
      if (!m_pRecvBuffer) return FALSE;

      // point the packet buffer to the receive buffer
      m_Packet.pBuffer = (void*)m_pRecvBuffer;

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Close winsock service
    //
    // ------------------------------------------------------------------------------------------------
    CNetClient::~CNetClient()
    {
      m_dwRefCount--;

      // disconnect
      Disconnect();

      // remove properties from our window
      RemoveProp(m_hWnd, "netclient");

      // destroy the window
      DestroyWindow(m_hWnd);

      if (!m_dwRefCount)
      {
        // close sockets service
        WSACleanup();
      }

      // free resources
      if (m_pRecvBuffer) delete m_pRecvBuffer;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Connect to a host
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetClient::Connect(char *szHost, int iPort)
    {
      // refuses to connect if not initialized
      if (!m_bInitialized) return FALSE;

      // disconnect from any previous host
      Disconnect();

      // store port
      m_iPort = iPort;

      // initialize address
      memset((LPSTR)&m_MyAddr, 0, sizeof(m_MyAddr));
      m_MyAddr.sin_family = PF_INET;
      m_MyAddr.sin_port = htons(m_iPort);

      // resolve host
      memset(m_szHostData,0,sizeof(m_szHostData));
      m_hResolver = WSAAsyncGetHostByName(m_hWnd, NET_RESOLVE, szHost, m_szHostData, MAXGETHOSTSTRUCT);
      if (!m_hResolver)
        return FALSE;

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Continue the connect process to a host
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetClient::ContinueConnect(struct hostent *phe)
    {
      int nRc;

      // check if resolved correctly
      if (phe == NULL) return FALSE;

      // finish filling socket info
      if (phe->h_addr_list[0] != NULL)
        memcpy(&m_MyAddr.sin_addr, *(phe->h_addr_list), sizeof(*(phe->h_addr_list)));
      else
        return FALSE;

      // --------------------------------------------------------------------------
      // start connection
      // --------------------------------------------------------------------------

      // user wants to connect through UDP.
      if(m_Sock == INVALID_SOCKET)
      {

        if (m_SockType == stUDP)
        {
          // UDP
          m_Sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        }
        else
          // TCP
          m_Sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

        if(m_Sock != SOCKET_ERROR)
        {
          // set non-blocking bits
          unsigned long argp = 1;
          //ioctlsocket(m_Sock, FIONBIO, &argp);
        }
        else
        {
          // error while initializing UDP
          m_Sock = INVALID_SOCKET;
          return FALSE;
        }

      }

      // request an asynchronous connection
      nRc = WSAAsyncSelect(m_Sock,
          m_hWnd,                  // our window procedure will be the recipient
          NET_SELECT,              // message to send
          FD_CONNECT |             // tell when a connection is done
          FD_READ  |               // tell when new data's here
          FD_WRITE |               // tell when there's buffer space
          FD_CLOSE);               // tell if the other side closes

      // try connecting to host
      nRc = connect(m_Sock, (const struct sockaddr *)&m_MyAddr, sizeof(m_MyAddr));

      if (nRc == SOCKET_ERROR)
      {

        nRc = WSAGetLastError();
        switch (nRc)
        {
          default:
          {
            // really failed
            m_Sock = INVALID_SOCKET;
            return FALSE;
          }

          case WSAEWOULDBLOCK:
          {
            // ok, connect is queued...
            // just ignore it.
            break;
          }

        }

      }

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Disconnect
    //
    // ------------------------------------------------------------------------------------------------
    void CNetClient::Disconnect()
    {
      // turn off UDP if it's running.
      if(m_Sock != INVALID_SOCKET)
      {
        closesocket(m_Sock);
        m_Sock = INVALID_SOCKET;
      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Process connection events, that can be either TCP or UDP.
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetClient::Select(int wParam, int lParam)
    {
      int nRc, i;
      unsigned long data = 0;
      unsigned long four_byte;
      int namelen = sizeof(m_HostAddr), ilen = sizeof(unsigned long);

      // get the involved socket
      SOCKET sock = (SOCKET)wParam;

      switch(WSAGETSELECTEVENT(lParam))
      {

        case FD_CONNECT:
        {
          //
          // only TCP connections pass here
          //
          if (m_userProc) m_userProc(this, m_pUserData, NET_CONNECTED);
          break;
        }

        case FD_READ:
        {
          //
          // check if really there is need for reading
          //

          nRc = ioctlsocket(sock, FIONREAD, (u_long *)&data);
          if(nRc == SOCKET_ERROR)
            return FALSE;
          if(data <= 0 )
            return FALSE;

          // TCP doesn't fill in the from address on
          // recvfrom().  Find out if this is UDP or
          // tcp, and use an appropriate mechanism to
          // find out who the other end is.

          i = sizeof(int);
          getsockopt(sock, SOL_SOCKET, SO_TYPE, (char *)&nRc, (LPINT)&i);

          if(nRc == SOCK_DGRAM)
          {
            //
            // UDP datagram
            //
            nRc = recvfrom(sock, (LPSTR)&four_byte, (int)ilen, MSG_PEEK, (struct sockaddr *)&m_HostAddr, (LPINT)&namelen);
            if(nRc == SOCKET_ERROR)
            {
              nRc = WSAGetLastError();
              if(nRc != WSAEMSGSIZE)
                return FALSE;
            }
          }
          else
          {
            //
            // TCP packet
            //
            nRc = getpeername(sock, (struct sockaddr *)&m_HostAddr, (LPINT)&namelen);
          }
          if(nRc == SOCKET_ERROR)
            return FALSE;

          data = m_HostAddr.sin_addr.s_addr;

          // tell client there is data waiting
          if (m_userProc) m_userProc(this, m_pUserData, NET_NEWDATA);

          break;
        }

        case FD_CLOSE:
        {
          //
          // Only TCP pass here.
          //
          // the other side closed the connection.  close the socket,
          // to clean up internal resources.
          //

          // receive the disconnection message
          nRc = recvfrom(sock, (LPSTR)&four_byte, (int)ilen, MSG_PEEK, (struct sockaddr *)&m_HostAddr, (LPINT)&namelen);

          // if there is a last bit of data, let user read it.
          if (nRc > 0)
            if (m_userProc) m_userProc(this, m_pUserData, NET_NEWDATA);

          Disconnect();
          if (m_userProc) m_userProc(this, m_pUserData, NET_DISCONNECTION);

          break;
        }

      }   // end: switch(WSAGETSELECTEVENT(lParam))

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Send a message to the host.
    //
    // ------------------------------------------------------------------------------------------------
    void CNetClient::Send(void *Msg, int MsgLen)
    {
      if (m_SockType == stTCP)
      {
        //
        // use TCP/IP send()
        //
        send(m_Sock, (const char *)Msg, MsgLen, 0);
      }
      else
      {
        //
        // use UDP sendto()
        //
        sendto(m_Sock, (const char *)Msg, MsgLen, 0, (struct sockaddr *)&m_HostAddr, sizeof(m_HostAddr));
      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // There is data waiting to be read.
    // Receive that.
    // Both TCP/IP and UDP pass here, after been 'selected'.
    //
    // ------------------------------------------------------------------------------------------------
    NETPACKET* CNetClient::Read()
    {
      int nRc, nTc=0;
      int ilen = sizeof(struct sockaddr_in);
      char *p = (char*)m_pRecvBuffer;

      do
      {

        if (m_SockType == stUDP)
        {
          nRc = recvfrom( m_Sock,
                          p,
                          m_iRecvBufferSize-nTc,
                          0,
                          (struct sockaddr *)&m_HostAddr,
                          (LPINT)&ilen
                        );
        }
        else
        {
          nRc = recv(m_Sock, p, m_iRecvBufferSize-nTc, 0);
        }

        if (nRc > 0)
        {
          nTc += nRc;
          p += nRc;
        }

      } while (nRc > 0);

      if (nTc > 0)
        m_Packet.iLength = nTc;
      else
        m_Packet.iLength = nRc;

      return &m_Packet;
    }

Currently browsing [vnnetclass.zip] (17,401 bytes) - [netclient.h] - (4,666 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #ifndef _NETCLIENT_H_
    #define _NETCLIENT_H_

      #include <time.h>
      #include "net.h"

      // ----------------------------------------------------------------------------------------------

      // user's callback
      #define USR_CLNTCALLBACK(userProc) void (*userProc)(CNetClient *pClient, void *pUserData, int iMsg)

      // ----------------------------------------------------------------------------------------------
      // NetClient class
      // ----------------------------------------------------------------------------------------------
      class CNetClient
      {
        // internal window procedure
        friend LRESULT CALLBACK NetClientWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

        private:

          // reference count
          static DWORD        m_dwRefCount;

          // netclient hidden window
          HWND                m_hWnd;

          // connection information
          SOCKET              m_Sock;
          enSockType          m_SockType;
          int                 m_iPort;

          // receive buffer
          char*               m_pRecvBuffer;
          int                 m_iRecvBufferSize;

          // a network packet
          NETPACKET           m_Packet;

          // client ip address
          struct sockaddr_in  m_MyAddr;

          // host ip address
          struct sockaddr_in  m_HostAddr;

          // flag if the class is correctly initialized
          BOOL                m_bInitialized;

          // user callback procedure
          USR_CLNTCALLBACK(m_userProc);
          void*               m_pUserData;

          // used by the non-blocking gethostbyname()
          HANDLE              m_hResolver;
          char                m_szHostData[MAXGETHOSTSTRUCT];

          // ------------------------------------------------------------------------------------------
          // private methods
          // ------------------------------------------------------------------------------------------

          BOOL        Init(enSockType SockType, USR_CLNTCALLBACK(userProc), void *pUserData, int iBufferSize);
          BOOL        Select(int wParam, int lParam);

          // after resolving the host, this will
          // be called to continue connection process
          BOOL CNetClient::ContinueConnect(struct hostent *phe);

        public:

          // ------------------------------------------------------------------------------------------
          // constructor - user must provide a callback function address to receive network events.
          // a NULL callback address is accepted but in this case the user can't receive warnings
          // about connection, disconnection, new packets, etc.
          // optionally can be passed a pointer to any desired user data. this pointer will be passed
          // back in any call to the user's callback.
          // ------------------------------------------------------------------------------------------

          CNetClient(enSockType SockType, USR_CLNTCALLBACK(userProc), void *pUserData, int iBufferSize=1024);

          // ------------------------------------------------------------------------------------------
          // destructor - closes connection, clean resources.
          // ------------------------------------------------------------------------------------------

          virtual ~CNetClient();

          // ------------------------------------------------------------------------------------------
          // connect to a hostname through specified port.
          // ------------------------------------------------------------------------------------------

          BOOL        Connect(char *szHost, int iPort);

          // ------------------------------------------------------------------------------------------
          // disconnects from actual host.
          // ------------------------------------------------------------------------------------------

          void        Disconnect();

          // ------------------------------------------------------------------------------------------
          // read and return a pointer to a network packet.
          // ------------------------------------------------------------------------------------------

          NETPACKET*  Read();

          // ------------------------------------------------------------------------------------------
          // send data to the actual host.
          // ------------------------------------------------------------------------------------------

          void        Send(void *Msg, int MsgLen);
      };

    #endif

Currently browsing [vnnetclass.zip] (17,401 bytes) - [netserver.cpp] - (19,931 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #include <windows.h>
    #include "netserver.h"

    DWORD CNetServer::m_dwRefCount = 0;

    // ------------------------------------------------------------------------------------------------
    //
    // Server Window Procedure
    //
    // ------------------------------------------------------------------------------------------------
    LRESULT CALLBACK NetWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
    {
      // get the pointer to the netserver class
      CNetServer *pServer = (CNetServer*)GetProp(hWnd, "netserver");

      // handle the message
      switch (uMessage)
      {
        case NET_SELECT:
        {
          pServer->Select(wParam, lParam);
          break;
        }
      }

      // return default window procedure result
      return DefWindowProc(hWnd,uMessage,wParam,lParam);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Construction
    //
    // ------------------------------------------------------------------------------------------------
    CNetServer::CNetServer(enSockType SockType, USR_SRVRCALLBACK(userProc), void *pUserData, int iListenPort, int iMaxClients, int iBufferSize, int iTimeout)
    {
      m_bInitialized = FALSE;
      m_dwRefCount++;

      // try to initialize buffers
      if ( !Init(iMaxClients, iBufferSize) ) throw (0);

      // try to create hidden window
      if ( !(m_hWnd = NetMakeWindow("netserver",m_dwRefCount,64,32,WS_OVERLAPPEDWINDOW,NetWndProc)) ) throw (1);

      // put our address in the window properties
      if ( !SetProp(m_hWnd, "netserver", (HANDLE)this) ) throw (2);

      m_bInitialized = TRUE;

      // try to start listening for connections
      if ( !Listen(SockType, userProc, pUserData, iListenPort, iTimeout) ) throw (3);
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Initialization
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetServer::Init(int iMaxClients, int iBufferSize)
    {
      if (m_dwRefCount == 1)
      {
        WSADATA wsaData;
        WORD wVersionRequested = MAKEWORD(1,1);
        WSAStartup(wVersionRequested, &wsaData);
      }

      // clear listening sockets
      m_UDPInSock = INVALID_SOCKET;
      m_TCPInSock = INVALID_SOCKET;

      // initialize clients array
      m_iMaxClients = iMaxClients;
      m_iNumClients = 0;
      m_pClients = new CONN_INFO[m_iMaxClients];
      if (!m_pClients) return FALSE;

      // initialize buffers
      m_iRecvBufferSize = iBufferSize;
      m_pRecvBuffer = new char[m_iRecvBufferSize];
      if (!m_pRecvBuffer) return FALSE;

      // point netpacket buffer to the receive buffer
      m_Packet.pBuffer = m_pRecvBuffer;

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Close winsock service
    //
    // ------------------------------------------------------------------------------------------------
    CNetServer::~CNetServer()
    {
      m_dwRefCount--;

      // stop listening
      StopListening();

      // remove properties from our window
      RemoveProp(m_hWnd, "netserver");

      // destroy the window
      DestroyWindow(m_hWnd);

      if (!m_dwRefCount)
      {
        // close sockets service
        WSACleanup();
      }

      // free resources
      if (m_pClients) delete m_pClients;
      if (m_pRecvBuffer) delete m_pRecvBuffer;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Start listening for client connections
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetServer::Listen(enSockType SockType, USR_SRVRCALLBACK(userProc), void *pUserData, int iListenPort, int iTimeout)
    {
      int nRc;
      int c;

      // refuses to listen if not initialized
      if (!m_bInitialized) return FALSE;

      // stop any listening mode
      StopListening();

      // store listening information
      m_SockType = SockType;
      m_userProc = userProc;
      m_pUserData = pUserData;
      m_iListenPort = iListenPort;
      m_iTimeout = iTimeout;

      // clear any remaining client
      for(c=0; c<m_iMaxClients; c++)
        m_pClients[c].used = 0;

      // get server address
      memset((LPSTR)&m_MyAddr, 0, sizeof(m_MyAddr));
      m_MyAddr.sin_family = PF_INET;
      m_MyAddr.sin_port = htons(m_iListenPort);

      // --------------------------------------------------------------------------
      // listen for UDP connections
      // --------------------------------------------------------------------------

      if(m_SockType == stUDP)
      {

        // user wants to listen for UDP.
        if(m_UDPInSock == INVALID_SOCKET)
        {

          // its not already running, so start it
          m_UDPInSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

          if(m_UDPInSock != SOCKET_ERROR)
          {
            nRc = bind(m_UDPInSock, (struct sockaddr *)&m_MyAddr, sizeof(m_MyAddr));

            //
            // Use WSAAsyncSelect() to notify ourselves of network
            // events.  Since this is UDP, we'll never see FD_CONNECT,
            // FD_ACCEPT, or FD_CLOSE, but we can still register for
            // them.
            //

            nRc = WSAAsyncSelect(m_UDPInSock,
                                 m_hWnd,                  // our window procedure will be the recipient
                                 NET_SELECT,              // message to send
                                 FD_READ  |               // tell when new data's here
                                 FD_WRITE |               // tell when there's buffer space
                                 FD_CONNECT |             // tell when the below connect is done
                                 FD_ACCEPT |
                                 FD_CLOSE);               // tell if the other side closes

          }
          else
          {
            // error while initializing UDP
            m_UDPInSock = INVALID_SOCKET;
            return FALSE;
          }

        }

      }

      // --------------------------------------------------------------------------
      // listen for TCP connections
      // --------------------------------------------------------------------------

      if(m_SockType == stTCP)
      {

        // user wants to listen for TCP.
        if(m_TCPInSock == INVALID_SOCKET)
        {

          // it's not already running, so start it
          m_TCPInSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

          if(m_TCPInSock != SOCKET_ERROR)
          {
            nRc = bind(m_TCPInSock, (struct sockaddr *)&m_MyAddr, sizeof(m_MyAddr));

            nRc = listen(m_TCPInSock, 2);

            //
            // Use WSAAsyncSelect() to notify ourselves of network
            // events.  This is TCP, so we could see all of the
            // notifications.  However, since we are listening,
            // we'll never see FD_CONNECT on this socket.
            // them.
            //

            nRc = WSAAsyncSelect(m_TCPInSock,
                                 m_hWnd,         // our window procedure will be the recipient
                                 NET_SELECT,
                                 FD_READ  |
                                 FD_WRITE |
                                 FD_CONNECT |
                                 FD_ACCEPT |
                                 FD_CLOSE);

          }
          else
          {
            // error while initializing TCP
            m_TCPInSock = INVALID_SOCKET;
            return FALSE;
          }

        }

      }

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Stop listening
    //
    // ------------------------------------------------------------------------------------------------
    void CNetServer::StopListening()
    {
      // turn off UDP if it's running.
      if(m_UDPInSock != INVALID_SOCKET)
      {
        closesocket(m_UDPInSock);
        m_UDPInSock = INVALID_SOCKET;
      }

      // turn off TCP if it's running.
      if(m_TCPInSock != INVALID_SOCKET)
      {
        closesocket(m_TCPInSock);
        m_TCPInSock = INVALID_SOCKET;
      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Process incoming connections, that can be either TCP or UDP.
    // If TCP, accepts connection and increments NumClients.
    // If UDP, simply receive the message and close the socket.
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetServer::Select(int wParam, int lParam)
    {
      int nRc, i;
      unsigned long data = 0;
      unsigned long four_byte;
      struct sockaddr_in rem_addr;
      int namelen = sizeof(rem_addr), ilen = sizeof(unsigned long);
      int clientidx;

      // get the involved socket
      SOCKET sock = (SOCKET)wParam;

      switch(WSAGETSELECTEVENT(lParam))
      {

        case FD_CONNECT:
        {
          //
          // only TCP connections pass here
          //
          break;
        }

        case FD_ACCEPT:
        {
          //
          // (only TCP connections pass here)
          // a new TCP connection has come in.  Accept it, set
          // up the socket info
          //

          // accept the incoming connection
          sock = accept(sock, NULL, NULL);

          // we must get the address of the peer
          nRc = getpeername(sock, (struct sockaddr *)&rem_addr, (LPINT) &namelen);

          // if don't, refuse connection
          if(nRc == SOCKET_ERROR)
          {
            closesocket(sock);
            // tell user about the refused connection
            if (m_userProc) m_userProc(this, m_pUserData, NET_REFCONNECTION, -1);
            return FALSE;
          }

          //?
          //data = rem_addr.sin_addr.s_addr;

          //
          // if this is a new connection, we won't have a local handle
          // yet.  Just pass it on to the new connection routine
          //
          if ((clientidx = FindClientIdx(sock)) == -1)
          {
            // the method will tell user about the new connection
            NewConnection(sock);
          }
          else
          {
            // we don't need to do anything, just
            // allow the socket to get it's FD_READ
            // which will be next
          }
          break;
        }

        case FD_READ:
        {
          //
          // check if really there is need for reading
          //
          nRc = ioctlsocket(sock, FIONREAD, (u_long *)&data);
          if(nRc == SOCKET_ERROR)
            return FALSE;
          if(data <= 0 )
            return FALSE;

          // TCP doesn't fill in the from address on
          // recvfrom().  Find out if this is UDP or
          // tcp, and use an appropriate mechanism to
          // find out who the other end is.

          i = sizeof(int);
          getsockopt(sock, SOL_SOCKET, SO_TYPE, (char *)&nRc, (LPINT)&i);

          if(nRc == SOCK_DGRAM)
          {
            //
            // UDP datagram
            //
            nRc = recvfrom(sock, (LPSTR)&four_byte, (int)ilen, MSG_PEEK, (struct sockaddr *)&rem_addr, (LPINT)&namelen);
            if(nRc == SOCKET_ERROR)
            {
              nRc = WSAGetLastError();
              if(nRc != WSAEMSGSIZE)
                return FALSE;
            }
          }
          else
          {
            //
            // TCP packet
            //
            nRc = getpeername(sock, (struct sockaddr *)&rem_addr, (LPINT)&namelen);
          }
          if(nRc == SOCKET_ERROR)
            return FALSE;

          data = rem_addr.sin_addr.s_addr;

          //
          // if it's a new connection, we won't have a local handle
          // yet.  Just pass it on to the new connection routine
          //
          if ((clientidx = FindClientIdx(sock)) == -1)
          {
            // it's a new connection.
            // connect and tell user about the new connection
            NewConnection(sock);
          }
          else
          {
            // it isn't a new connection.
            // tell client there is data waiting
            if (m_userProc) m_userProc(this, m_pUserData, NET_NEWDATA, clientidx);
          }

          break;
        }

        case FD_CLOSE:
        {
          //
          // Only TCP pass here.
          //
          // the other side closed the connection.  close the socket,
          // to clean up internal resources.
          //

          // receive the disconnection message
          recvfrom(sock, (LPSTR)&four_byte, (int)ilen, MSG_PEEK, (struct sockaddr *)&rem_addr, (LPINT)&namelen);

          // find the client connection info
          clientidx = FindClientIdx(sock);
          if (clientidx != -1)
          {
            // free client slot.
            m_pClients[clientidx].used = 0;
            m_pClients[clientidx].sock = INVALID_SOCKET;
          }

          // close socket and update client counting
          closesocket(sock);
          m_iNumClients--;

          // tell user about the disconnection
          if (m_userProc) m_userProc(this, m_pUserData, NET_DISCONNECTION, clientidx);

          break;
        }

      }   // end: switch(WSAGETSELECTEVENT(lParam))

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // There is data waiting to be read.
    // Receive that.
    // Both TCP/IP and UDP pass here, after been 'selected'.
    //
    // ------------------------------------------------------------------------------------------------
    NETPACKET* CNetServer::ReadMessageFromClient(int iClientIdx)
    {
      int nRc;
      struct sockaddr rem_addr;
      int ilen = sizeof(struct sockaddr_in);
      CONN_INFO *pClient;

      // safe checking
      if (iClientIdx < 0 || iClientIdx >= m_iMaxClients) return NULL;

      // get the client
      pClient = &m_pClients[iClientIdx];

      // more safe checking
      if (!pClient->used) return NULL;

      nRc = recvfrom( pClient->sock,
                      m_pRecvBuffer,
                      m_iRecvBufferSize,
                      0,
                      (struct sockaddr *)&rem_addr,
                      (LPINT)&ilen
                    );

      m_Packet.iLength = nRc;

      return &m_Packet;

      //### NOTE FOR UDP MESSAGES:
      // i think the best thing to do at this stage is:
      // if the first bytes of the message are not a ClientIdx number,
      // allocate a new Client entry for him and
      // answer to the client with his ClientIdx number.
      // the client them should send all his forthcoming messages
      // with the first bytes containing his ClientIdx id.
      // if his message contains the ClientIdx, we know that client
      // and can process his message.
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Register an incoming call as a new connection.
    // ### Currently it's being called only from TCP calls by selector,
    // but should be called for both.
    // to register UDP calls, i need to implement some timing control to
    // figure out when the client is not communicating anymore.
    //
    // ------------------------------------------------------------------------------------------------
    BOOL CNetServer::NewConnection(SOCKET ServerSocket)
    {
      int clientidx;
      int nRc;
      CONN_INFO *conn_info;
      int sz = sizeof(int);
      int type;
      unsigned long llen;
      int ilen;

      // refuse only if server is full.
      if (m_iNumClients == m_iMaxClients)
      {
        closesocket(ServerSocket);
        // tell user about the refused connection
        if (m_userProc) m_userProc(this, m_pUserData, NET_REFCONNECTION, -1);
        return FALSE;
      }

      // seek for an empty slot to accomodate this client.
      for(clientidx=0; clientidx<m_iMaxClients; clientidx++)
        if (!m_pClients[clientidx].used)
          break;

      // should never i==m_iMaxClients!
      if (clientidx==m_iMaxClients) return FALSE;

      // use this slot.
      conn_info = &m_pClients[clientidx];

      // must determine if we are dealing with a TCP (stream)
      // or UDP (dgram) incoming connection.  We could have just
      // passed it in as a flag, but this works as well.
      nRc = getsockopt(ServerSocket, SOL_SOCKET, SO_TYPE,(LPSTR)(LPINT)&type, (LPINT) &sz);

      if(nRc == SOCKET_ERROR)
      {
        nRc = WSAGetLastError();
        return FALSE;
      }

      //
      // initialize the connection structure
      //

      conn_info->sock = INVALID_SOCKET;

      if(type == SOCK_STREAM)
      {

        //
        // it's a TCP socket.  fill the conn_info structure.
        //
        conn_info->protocol = IPPROTO_TCP;
        ilen = sizeof(struct sockaddr_in);
        nRc = getpeername(ServerSocket, (struct sockaddr *)&(conn_info->rem_addr),(LPINT)&ilen);
        if(nRc == SOCKET_ERROR)
          return FALSE;

        // mark this slot as used.
        m_iNumClients ++;
        conn_info->used = 1;
      }
      else if (type == SOCK_DGRAM)
      {

        //
        // it's a UDP socket. We'll need to do a recvfrom() to
        // get the name of the other side, but leave the data there.
        // that will cause another FD_READ to be sent, and then
        // the message will be displayed in the normal manner
        //

        conn_info->protocol = IPPROTO_UDP;

        // first, get the size of the incoming message
        nRc = ioctlsocket(ServerSocket, FIONREAD, (u_long *)&llen);
        if(nRc == SOCKET_ERROR)
          return FALSE;

        ilen = sizeof(struct sockaddr_in);

        //
        // using MSG_PEEK causes the data to be read and left
        // in the queue, so another FD_READ will be issued.
        // by that time, everything is set up, so the right
        // thing will happen.
        //
        nRc = recvfrom(ServerSocket, m_pRecvBuffer, (int)ilen, MSG_PEEK,
                  (struct sockaddr *)&(conn_info->rem_addr),
                  (LPINT)&ilen);

        if(nRc == SOCKET_ERROR)
        {
          nRc = WSAGetLastError();
          return FALSE;
        }

        // RecvBuf contains the initial data sent from client,
        // using UDP datagram.

      }

      //
      // Associate the conn_info with it
      //
      conn_info->sock = ServerSocket;
      conn_info->lasttick = GetTickCount();

      // tell user about the new connection
      if (m_userProc) m_userProc(this, m_pUserData, NET_NEWCONNECTION, clientidx);

      return TRUE;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Seek for a client, given it's socket.
    //
    // ------------------------------------------------------------------------------------------------
    int CNetServer::FindClientIdx(SOCKET Socket)
    {
      int c;

      // search by socket
      for (c=0; c<m_iMaxClients; c++)
        if (Socket == m_pClients[c].sock)
          return c;

      return -1;
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Send a message to a client.
    //
    // ------------------------------------------------------------------------------------------------
    void CNetServer::SendMessageToClient(int ClientIdx, void *Msg, int MsgLen)
    {
      CONN_INFO *Client = &m_pClients[ClientIdx];

      if (Client->protocol == IPPROTO_TCP)
      {
        //
        // use TCP/IP send()
        //
        send(Client->sock, (const char *)Msg, MsgLen, 0);
      }
      else
      {
        //
        // use UDP sendto()
        //
        sendto(Client->sock, (const char *)Msg, MsgLen, 0, (struct sockaddr *)&Client->rem_addr, sizeof(Client->rem_addr));
      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Broadcast a message to all clients
    //
    // ------------------------------------------------------------------------------------------------
    void CNetServer::BroadcastMessage(void *Msg, int MsgLen)
    {
      int c;

      for (c=0; c<m_iMaxClients; c++)
      {
        if (m_pClients[c].used)
          SendMessageToClient(c,Msg,MsgLen);
      }
    }

    // ------------------------------------------------------------------------------------------------
    //
    // Return a pointer to a client connection info
    //
    // ------------------------------------------------------------------------------------------------
    CONN_INFO*  CNetServer::Client(int iClientIdx)
    {
      if (iClientIdx < 0 || iClientIdx >= m_iMaxClients) return NULL;
      return (&m_pClients[iClientIdx]);
    }

Currently browsing [vnnetclass.zip] (17,401 bytes) - [netserver.h] - (3,507 bytes)

    //====----------------------- --- --   -      -
    // MULTIUSER NETWORK MANAGEMENT
    // TCP/IP and UDP support.
    // ©2000-2002, Vander Nunes
    //======----------------------------- --- --   -      -

    #ifndef _NETSERVER_H_
    #define _NETSERVER_H_

      #include <time.h>
      #include "net.h"

      // ----------------------------------------------------------------------------------------------

      // user's callback
      #define USR_SRVRCALLBACK(userProc) void (*userProc)(CNetServer *pServer, void *pUserData, int iMsg, int iClientIdx)

      // connection information
      typedef struct _conn
      {
        struct sockaddr_in rem_addr;    // net address of other end
        SOCKET sock;                    // socket(s) allocated for net traffic, waiting for a response.
        int    protocol;                // protocol to use when calling client
        long   lasttick;                // last tick when we heard something from this client
        char   used;                    // 0 = empty slot
      } CONN_INFO;

      // ----------------------------------------------------------------------------------------------
      // NetServer class
      // ----------------------------------------------------------------------------------------------
      class CNetServer
      {
        // internal window procedure
        friend LRESULT CALLBACK NetWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

        private:

          // reference count
          static DWORD        m_dwRefCount;

          // netserver hidden window
          HWND                m_hWnd;

          enSockType          m_SockType;
          int                 m_iListenPort;
          int                 m_iMaxClients;
          CONN_INFO*          m_pClients;
          int                 m_iNumClients;

          char*               m_pRecvBuffer;
          int                 m_iRecvBufferSize;
          NETPACKET           m_Packet;

          SOCKET              m_UDPInSock;
          SOCKET              m_TCPInSock;
          BOOL                m_bInitialized;

          // default timeout to auto-disconnect clients.
          // this is useful to handle UDP connections too.
          // ### because my time constraints, I didn't finish Timeout handling yet.
          int                 m_iTimeout;

          // server ip address
          struct sockaddr_in  m_MyAddr;

          // user callback procedure
          USR_SRVRCALLBACK(m_userProc);
          void               *m_pUserData;

          // ------------------------------------------------------------------------------------------
          // initialize winsock
          // ------------------------------------------------------------------------------------------

          BOOL        Init(int iMaxClients, int iBufferSize);
          BOOL        Select(int wParam, int lParam);
          BOOL        NewConnection(SOCKET ServerSocket);
          int         FindClientIdx(SOCKET Socket);

        public:

          CNetServer(enSockType SockType, USR_SRVRCALLBACK(userProc), void *pUserData, int iListenPort=1970, int iMaxClients=32, int iBufferSize=1024, int iTimeout=60000);
          virtual ~CNetServer();

          BOOL        Listen(enSockType SockType, USR_SRVRCALLBACK(userProc), void *pUserData, int iListenPort, int iTimeout=60000);
          void        StopListening();

          NETPACKET*  ReadMessageFromClient(int iClientIdx);
          void        SendMessageToClient(int ClientIdx, void *Msg, int MsgLen);
          void        BroadcastMessage(void *Msg, int MsgLen);

          CONN_INFO*  Client(int iClientIdx);

      };

    #endif

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.