|
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.
|