www.pudn.com > ServerClient_Socket.rar > SocketComm.cpp


/////////////////////////////////////////////////////////////////////////////// 
//	File:		SocketComm.cpp 
//	Version:	1.3 
// 
//	Author:		Ernest Laurentin 
//	E-mail:		elaurentin@netzero.net 
// 
//	Implementation of the CSocketComm and associated classes. 
// 
//	This code may be used in compiled form in any way you desire. This 
//	file may be redistributed unmodified by any means PROVIDING it is 
//	not sold for profit without the authors written consent, and 
//	providing that this notice and the authors name and all copyright 
//	notices remains intact. 
// 
//	This file is provided "as is" with no expressed or implied warranty. 
//	The author accepts no liability for any damage/loss of business that 
//	this c++ class may cause. 
// 
//	Version history 
// 
//	1.0	- Initial release. 
//	1.1 - Add support for Smart Addressing mode 
//  1.2 - Fix various issues with address list (in UDP mode) 
//  1.3 - Fix bug when sending message to broadcast address 
/////////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include  
#include  
#include  
#include  
#include "SocketComm.h" 
 
const DWORD DEFAULT_TIMEOUT = 100L; 
struct stMessageProxy 
{ 
  SockAddrIn	address; 
  BYTE			data[BUFFER_SIZE]; 
}; 
 
/////////////////////////////////////////////////////////////////////////////// 
// SockAddrIn Struct 
 
/////////////////////////////////////////////////////////////////////////////// 
// Copy 
SockAddrIn& SockAddrIn::Copy(const SockAddrIn& sin) 
{ 
	memcpy(this, &sin, Size()); 
	return *this; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsEqual 
bool SockAddrIn::IsEqual(const SockAddrIn& sin) 
{ 
	// Is it Equal? - ignore 'sin_zero' 
	return (memcmp(this, &sin, Size()-sizeof(sin_zero)) == 0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsGreater 
bool SockAddrIn::IsGreater(const SockAddrIn& sin) 
{ 
	// Is it Greater? - ignore 'sin_zero' 
	return (memcmp(this, &sin, Size()-sizeof(sin_zero)) > 0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsLower 
bool SockAddrIn::IsLower(const SockAddrIn& sin) 
{ 
	// Is it Lower? - ignore 'sin_zero' 
	return (memcmp(this, &sin, Size()-sizeof(sin_zero)) < 0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// CreateFrom 
bool SockAddrIn::CreateFrom(LPCTSTR sAddr, LPCTSTR sService, int nFamily /*=AF_INET*/) 
{ 
	Clear(); 
	sin_addr.s_addr = htonl( CSocketComm::GetIPAddress(sAddr) ); 
	sin_port = htons( CSocketComm::GetPortNumber( sService ) ); 
	sin_family = nFamily; 
	return !IsNull(); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// Construct & Destruct 
CSocketComm::CSocketComm() : 
	m_bServer(false), m_bSmartAddressing(false), m_bBroadcast(false), 
	m_hComm(INVALID_HANDLE_VALUE), m_hThread(NULL), m_hMutex(NULL) 
{ 
 
} 
 
CSocketComm::~CSocketComm() 
{ 
	StopComm(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// Members 
/////////////////////////////////////////////////////////////////////////////// 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsOpen 
bool CSocketComm::IsOpen() const 
{ 
	return ( INVALID_HANDLE_VALUE != m_hComm ); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsStart 
bool CSocketComm::IsStart() const 
{ 
	return ( NULL != m_hThread ); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsServer 
bool CSocketComm::IsServer() const 
{ 
	return m_bServer; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsBroadcast 
bool CSocketComm::IsBroadcast() const 
{ 
	return m_bBroadcast; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// IsSmartAddressing 
bool CSocketComm::IsSmartAddressing() const 
{ 
	return m_bSmartAddressing; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetSocket 
SOCKET CSocketComm::GetSocket() const 
{ 
	return (SOCKET) m_hComm; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// LockList 
void CSocketComm::LockList() 
{ 
	if (NULL != m_hMutex) 
		WaitForSingleObject(m_hMutex, INFINITE); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// UnlockList 
void CSocketComm::UnlockList() 
{ 
	if (NULL != m_hMutex) 
		ReleaseMutex(m_hMutex); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// AddToList 
void CSocketComm::AddToList(const SockAddrIn& saddr_in) 
{ 
	LockList(); 
	m_AddrList.insert( m_AddrList.end(), saddr_in ); 
	UnlockList(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// RemoveFromList 
void CSocketComm::RemoveFromList(const SockAddrIn& saddr_in) 
{ 
	LockList(); 
	m_AddrList.remove( saddr_in ); 
	UnlockList(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// SetServerState 
void CSocketComm::SetServerState(bool bServer) 
{ 
	if (!IsStart()) 
		m_bServer = bServer; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// SetSmartAddressing : Address is included with message 
void CSocketComm::SetSmartAddressing(bool bSmartAddressing) 
{ 
	if (!IsStart()) 
		m_bSmartAddressing = bSmartAddressing; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnDataReceived 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				This function is PURE Virtual, you MUST overwrite it.  This is 
//				called every time new data is available. 
// PARAMETERS: 
/////////////////////////////////////////////////////////////////////////////// 
void CSocketComm::OnDataReceived(const LPBYTE lpBuffer, DWORD dwCount) 
{ 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnEvent 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				This function reports events & errors 
// PARAMETERS: 
//		UINT uEvent: can be one of the event value EVT_(events) 
/////////////////////////////////////////////////////////////////////////////// 
void CSocketComm::OnEvent(UINT uEvent) 
{ 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetPortNumber 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Returns a port number based on service name or port number string 
// PARAMETERS: 
//	LPCTSTR strServiceName: Service name or port string 
/////////////////////////////////////////////////////////////////////////////// 
USHORT CSocketComm::GetPortNumber( LPCTSTR strServiceName ) 
{ 
	LPSERVENT	lpservent; 
	USHORT		nPortNumber = 0; 
 
	if ( _istdigit( strServiceName[0] ) ) { 
		nPortNumber = (USHORT) _ttoi( strServiceName ); 
	} 
	else { 
#ifdef _UNICODE 
		char pstrService[HOSTNAME_SIZE]; 
		WideCharToMultiByte(CP_ACP, 0, pstrService, -1, strServiceName, sizeof(pstrService), NULL, NULL ); 
#else 
		LPCTSTR pstrDevice = strServiceName; 
#endif 
		// Convert network byte order to host byte order 
		if ( (lpservent = getservbyname( pstrDevice, NULL )) != NULL ) 
			nPortNumber = ntohs( lpservent->s_port ); 
	} 
 
	return nPortNumber; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetIPAddress 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Returns an IP address. 
//			- It tries to convert the string directly 
//			- If that fails, it tries to resolve it as a hostname 
// PARAMETERS: 
//	LPCTSTR strHostName: host name to get IP address 
/////////////////////////////////////////////////////////////////////////////// 
ULONG CSocketComm::GetIPAddress( LPCTSTR strHostName ) 
{ 
	LPHOSTENT	lphostent; 
	ULONG		uAddr = INADDR_NONE; 
	TCHAR       strLocal[HOSTNAME_SIZE] = { 0 }; 
 
	// if no name specified, get local 
	if ( NULL == strHostName ) 
	{ 
		GetLocalName(strLocal, sizeof(strLocal)); 
		strHostName = strLocal; 
	} 
 
#ifdef _UNICODE 
	char strHost[HOSTNAME_SIZE] = { 0 }; 
	WideCharToMultiByte(CP_ACP, 0, strHostName, -1, strHost, sizeof(strHost), NULL, NULL ); 
#else 
	LPCTSTR strHost = strHostName; 
#endif 
 
	// Check for an Internet Protocol dotted address string 
	uAddr = inet_addr( strHost ); 
 
	if ( (INADDR_NONE == uAddr) && (strcmp( strHost, "255.255.255.255" )) ) 
	{ 
		// It's not an address, then try to resolve it as a hostname 
		if ( lphostent = gethostbyname( strHost ) ) 
			uAddr = *((ULONG *) lphostent->h_addr_list[0]); 
	} 
	 
	return ntohl( uAddr ); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetLocalName 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Get local computer name.  Something like: "mycomputer.myserver.net" 
// PARAMETERS: 
//	LPTSTR strName: name of the computer is returned here 
//	UINT nSize: size max of buffer "strName" 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::GetLocalName(LPTSTR strName, UINT nSize) 
{ 
	if (strName != NULL && nSize > 0) 
	{ 
		char strHost[HOSTNAME_SIZE] = { 0 }; 
 
		// get host name, if fail, SetLastError is set 
		if (SOCKET_ERROR != gethostname(strHost, sizeof(strHost))) 
		{ 
			struct hostent* hp; 
			hp = gethostbyname(strHost); 
			if (hp != NULL)	{ 
				strcpy(strHost, hp->h_name); 
			} 
 
			// check if user provide enough buffer 
			if (strlen(strHost) > nSize) 
			{ 
				SetLastError(ERROR_INSUFFICIENT_BUFFER); 
				return false; 
			} 
 
			// Unicode conversion 
#ifdef _UNICODE 
			return (0 != MultiByteToWideChar(CP_ACP, 0, strHost, -1, strName, nSize, NULL, NULL )); 
#else 
			_tcscpy(strName, strHost); 
			return true; 
#endif 
		} 
	} 
	else 
		SetLastError(ERROR_INVALID_PARAMETER); 
	return false; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetLocalAddress 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Get TCP address of local computer in dot format ex: "127.0.0.0" 
// PARAMETERS: 
//	LPTSTR strAddress: pointer to hold address string, must be long enough 
//	UINT nSize: maximum size of this buffer 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::GetLocalAddress(LPTSTR strAddress, UINT nSize) 
{ 
	// Get computer local address 
	if (strAddress != NULL && nSize > 0) 
	{ 
		char strHost[HOSTNAME_SIZE] = { 0 }; 
 
		// get host name, if fail, SetLastError is called 
		if (SOCKET_ERROR != gethostname(strHost, sizeof(strHost))) 
		{ 
			struct hostent* hp; 
			hp = gethostbyname(strHost); 
			if (hp != NULL && hp->h_addr_list[0] != NULL) 
			{ 
				// Address is four bytes (32-bit) 
				if ( hp->h_length < 4) 
					return false; 
 
				// Convert address to . format 
				strHost[0] = 0; 
 
				// Create Address string 
				sprintf(strHost, "%u.%u.%u.%u", 
					(UINT)(((PBYTE) hp->h_addr_list[0])[0]), 
					(UINT)(((PBYTE) hp->h_addr_list[0])[1]), 
					(UINT)(((PBYTE) hp->h_addr_list[0])[2]), 
					(UINT)(((PBYTE) hp->h_addr_list[0])[3])); 
 
				// check if user provide enough buffer 
				if (strlen(strHost) > nSize) 
				{ 
					SetLastError(ERROR_INSUFFICIENT_BUFFER); 
					return false; 
				} 
 
			// Unicode conversion 
#ifdef _UNICODE 
				return (0 != MultiByteToWideChar(CP_ACP, 0, strHost, -1, strAddress, 
					nSize, NULL, NULL )); 
#else 
				_tcscpy(strAddress, strHost); 
				return true; 
#endif 
			} 
		} 
	} 
	else 
		SetLastError(ERROR_INVALID_PARAMETER); 
	return false; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// WaitForConnection 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Wait for a network connection.  Only for connection type of socket 
//				This function may fail, in this case it returns "INVALID_SOCKET" 
// PARAMETERS: 
//	SOCKET sock: a socket capable of receiving new connection (TCP: SOCK_STREAM) 
/////////////////////////////////////////////////////////////////////////////// 
SOCKET CSocketComm::WaitForConnection(SOCKET sock) 
{ 
	// Accept an incoming connection - blocking 
	// no information about remote address is returned 
	return accept(sock, 0, 0); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// ShutdownConnection 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Shutdown a connection and close socket.  This will force all 
//				transmission/reception to fail. 
// PARAMETERS: 
//	SOCKET sock: Socket to close 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::ShutdownConnection(SOCKET sock) 
{ 
	shutdown(sock, SD_BOTH); 
	return ( 0 == closesocket( sock )); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetSockName 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				retrieves the local name for a socket 
// PARAMETERS: 
//	SockAddrIn& saddr_in: object to store address 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::GetSockName(SockAddrIn& saddr_in) 
{ 
	if (IsOpen()) 
	{ 
		int namelen = saddr_in.Size(); 
		return (SOCKET_ERROR != getsockname(GetSocket(), (LPSOCKADDR)saddr_in, &namelen)); 
	} 
 
	return false; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetPeerName 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				retrieves the name of the peer to which a socket is connected 
// PARAMETERS: 
//	SockAddrIn& saddr_in: object to store address 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::GetPeerName(SockAddrIn& saddr_in) 
{ 
	if (IsOpen()) 
	{ 
		int namelen = saddr_in.Size(); 
		return (SOCKET_ERROR != getpeername(GetSocket(), (LPSOCKADDR)saddr_in, &namelen));	 
	} 
 
	return false; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// CreateSocket 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				This function creates a new socket for connection (SOCK_STREAM) 
//				or an connectionless socket (SOCK_DGRAM).  A connectionless 
//				socket should not call "accept()" since it cannot receive new 
//				connection.  This is used as SERVER socket 
// PARAMETERS: 
//	LPCTSTR strServiceName: Service name or port number 
//	int nFamily: address family to use (set to AF_INET) 
//	int nType: type of socket to create (SOCK_STREAM, SOCK_DGRAM) 
//	UINT uOptions: other options to use 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::CreateSocket(LPCTSTR strServiceName, int nFamily, int nType, UINT uOptions /* = 0 */) 
{ 
	// Socket is already opened 
	if ( IsOpen() ) 
		return false; 
 
	// Create a Socket that is bound to a specific service provide 
	// nFamily: (AF_INET) 
	// nType: (SOCK_STREAM, SOCK_DGRAM) 
	SOCKET sock = socket(nFamily, nType, 0); 
	if (INVALID_SOCKET != sock) 
	{ 
		if (uOptions & SO_REUSEADDR) 
		{ 
			// Inform Windows Sockets provider that a bind on a socket should not be disallowed 
			// because the desired address is already in use by another socket 
			BOOL optval = TRUE; 
			if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof( BOOL ) ) ) 
			{ 
				closesocket( sock ); 
				return false; 
			} 
		} 
 
		if (nType == SOCK_DGRAM) 
		{ 
			if (uOptions & SO_BROADCAST) 
			{ 
				// Inform Windows Sockets provider that broadcast messages are allowed 
				BOOL optval = TRUE; 
				if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char *) &optval, sizeof( BOOL ) ) ) 
				{ 
					closesocket( sock ); 
					return false; 
				} 
 
				// we may proceed 
				m_bBroadcast = true; 
			} 
 
			// we need mutex only for UDP - broadcast socket 
			m_hMutex = CreateMutex(NULL, FALSE, NULL); 
			if (NULL == m_hMutex) 
			{ 
				closesocket( sock ); 
				return false; 
			} 
		} 
 
		// Associate a local address with the socket 
		SockAddrIn sockAddr; 
		sockAddr.CreateFrom(NULL, strServiceName, nFamily); 
 
		if ( SOCKET_ERROR == bind(sock, (LPSOCKADDR)sockAddr, sockAddr.Size())) 
		{ 
			closesocket( sock ); 
			m_bBroadcast = false; 
			if (NULL != m_hMutex) 
				CloseHandle( m_hMutex ); 
			m_hMutex = NULL; 
			return false; 
		} 
 
		// Listen to the socket, only valid for connection socket 
		if (SOCK_STREAM == nType) 
		{ 
			if ( SOCKET_ERROR == listen(sock, SOMAXCONN)) 
			{ 
				closesocket( sock ); 
				return false; 
			} 
		} 
 
		// Success, now we may save this socket 
		m_hComm = (HANDLE) sock; 
	} 
 
	return (INVALID_SOCKET != sock); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// ConnectTo 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//				Establish connection with a server service or port 
// PARAMETERS: 
//	LPCTSTR strDestination: hostname or address to connect (in .dot format) 
//	LPCTSTR strServiceName: Service name or port number 
//	int nFamily: address family to use (set to AF_INET) 
//	int nType: type of socket to create (SOCK_STREAM, SOCK_DGRAM) 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::ConnectTo(LPCTSTR strDestination, LPCTSTR strServiceName, int nFamily, int nType) 
{ 
	// Socket is already opened 
	if ( IsOpen() ) 
		return false; 
 
	// Create a Socket that is bound to a specific service provide 
	// nFamily: (AF_INET) 
	// nType: (SOCK_STREAM, SOCK_DGRAM) 
	SOCKET sock = socket(nFamily, nType, 0); 
	if (INVALID_SOCKET != sock) 
	{ 
		// Associate a local address with the socket 
		SockAddrIn sockAddr; 
		if (false == sockAddr.CreateFrom(NULL, TEXT("0"), nFamily)) 
		{ 
			closesocket( sock ); 
			return false; 
		} 
 
		if ( SOCKET_ERROR == bind(sock, (LPSOCKADDR)sockAddr, sockAddr.Size() )) 
		{ 
			closesocket( sock ); 
			return false; 
		} 
 
		// Now get destination address & port 
		sockAddr.CreateFrom( strDestination, strServiceName ); 
 
		// try to connect - if fail, server not ready 
		if (SOCKET_ERROR == connect( sock, (LPSOCKADDR)sockAddr, sockAddr.Size())) 
		{ 
			closesocket( sock ); 
			return false; 
		} 
 
		// Success, now we may save this socket 
		m_hComm = (HANDLE) sock; 
	} 
	return (INVALID_SOCKET != sock); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// CloseComm 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Close Socket Communication 
// PARAMETERS: 
//		None 
/////////////////////////////////////////////////////////////////////////////// 
void CSocketComm::CloseComm() 
{ 
	if (IsOpen()) 
	{ 
		ShutdownConnection((SOCKET)m_hComm); 
		m_hComm = INVALID_HANDLE_VALUE; 
		m_bBroadcast = false; 
	} 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// WatchComm 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Starts Socket Communication Working thread 
// PARAMETERS: 
//		None 
/////////////////////////////////////////////////////////////////////////////// 
bool CSocketComm::WatchComm() 
{ 
	if (!IsStart()) 
	{ 
		if (IsOpen()) 
		{ 
			HANDLE hThread; 
			UINT uiThreadId = 0; 
			hThread = (HANDLE)_beginthreadex(NULL,	// Security attributes 
									  0,	// stack 
						SocketThreadProc,	// Thread proc 
									this,	// Thread param 
						CREATE_SUSPENDED,	// creation mode 
							&uiThreadId);	// Thread ID 
 
			if ( NULL != hThread) 
			{ 
				//SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); 
				ResumeThread( hThread ); 
				m_hThread = hThread; 
				return true; 
			} 
		} 
	} 
	return false; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// StopComm 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Close Socket and Stop Communication thread 
// PARAMETERS: 
//		None 
/////////////////////////////////////////////////////////////////////////////// 
void CSocketComm::StopComm() 
{ 
	// Close Socket 
	if (IsOpen()) 
	{ 
		CloseComm(); 
		Sleep(50); 
	} 
 
	// Kill Thread 
	if (IsStart()) 
	{ 
		if (WaitForSingleObject(m_hThread, 5000L) == WAIT_TIMEOUT) 
			TerminateThread(m_hThread, 1L); 
		CloseHandle(m_hThread); 
		m_hThread = NULL; 
	} 
 
	// Clear Address list 
	if (!m_AddrList.empty()) 
		m_AddrList.clear(); 
 
	// Destroy Synchronization objects 
	if (NULL != m_hMutex) 
	{ 
		CloseHandle( m_hMutex ); 
		m_hMutex = NULL; 
	} 
 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// ReadComm 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Reads the Socket Communication 
// PARAMETERS: 
//		LPBYTE lpBuffer: buffer to place new data 
//		DWORD dwSize: maximum size of buffer 
//		DWORD dwTimeout: timeout to use in millisecond 
/////////////////////////////////////////////////////////////////////////////// 
DWORD CSocketComm::ReadComm(LPBYTE lpBuffer, DWORD dwSize, DWORD dwTimeout) 
{ 
	_ASSERTE( IsOpen() ); 
	_ASSERTE( lpBuffer != NULL ); 
 
	if (lpBuffer == NULL || dwSize < 1L) 
		return 0L; 
 
	fd_set	fdRead  = { 0 }; 
	TIMEVAL	stTime; 
	TIMEVAL	*pstTime = NULL; 
 
	if ( INFINITE != dwTimeout ) { 
		stTime.tv_sec = dwTimeout/1000; 
		stTime.tv_usec = dwTimeout % 1000; 
		pstTime = &stTime; 
	} 
 
	SOCKET s = (SOCKET) m_hComm; 
	// Set Descriptor 
	if ( !FD_ISSET( s, &fdRead ) ) 
		FD_SET( s, &fdRead ); 
 
	// Select function set read timeout 
	DWORD dwBytesRead = 0L; 
	int res = select( s+1, &fdRead, NULL, NULL, pstTime ); 
	if ( res > 0) 
	{ 
		if (IsBroadcast() || IsSmartAddressing()) 
		{ 
			SockAddrIn sockAddr; 
			int nLen = sockAddr.Size(); 
			int nOffset = IsSmartAddressing() ? nLen : 0; // use offset for Smart addressing 
			if ( dwSize < (DWORD) nOffset)	// error - buffer to small 
			{ 
				SetLastError( ERROR_INVALID_USER_BUFFER ); 
				return -1L; 
			} 
			LPSTR lpszData = (LPSTR)(lpBuffer + nOffset); 
			res = recvfrom( s, lpszData, dwSize-nOffset, 0, (LPSOCKADDR)sockAddr, &nLen); 
 
			// clear 'sin_zero', we will ignore them with 'SockAddrIn' anyway! 
			memset(&sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero)); 
 
			// Lock the list... 
			LockList(); 
			m_AddrList.remove( sockAddr ); 
			 
			if ( res >= 0) 
			{ 
				// insert unique address 
				m_AddrList.insert(m_AddrList.end(), sockAddr); 
 
				if (IsSmartAddressing()) 
				{ 
					memcpy(lpBuffer, &sockAddr, sockAddr.Size()); 
					res += sockAddr.Size(); 
				} 
			} 
 
			UnlockList(); // unlock this object addresses-list 
		} 
		else 
		{ 
			res = recv( s, (LPSTR)lpBuffer, dwSize, 0); 
		} 
 
		dwBytesRead = (DWORD)((res > 0)?(res) : (-1L)); 
	} 
 
	return dwBytesRead; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// WriteComm 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		Writes data to the Socket Communication 
// PARAMETERS: 
//		const LPBYTE lpBuffer: data to write 
//		DWORD dwCount: maximum characters to write 
//		DWORD dwTimeout: timeout to use in millisecond 
/////////////////////////////////////////////////////////////////////////////// 
DWORD CSocketComm::WriteComm(const LPBYTE lpBuffer, DWORD dwCount, DWORD dwTimeout) 
{ 
	_ASSERTE( IsOpen() ); 
	_ASSERTE( NULL != lpBuffer ); 
 
	// Accept 0 bytes message 
	if (!IsOpen() || NULL == lpBuffer) 
		return 0L; 
 
	fd_set	fdWrite  = { 0 }; 
	TIMEVAL	stTime; 
	TIMEVAL	*pstTime = NULL; 
 
	if ( INFINITE != dwTimeout ) { 
		stTime.tv_sec = dwTimeout/1000; 
		stTime.tv_usec = dwTimeout % 1000; 
		pstTime = &stTime; 
	} 
 
	SOCKET s = (SOCKET) m_hComm; 
	// Set Descriptor 
	if ( !FD_ISSET( s, &fdWrite ) ) 
		FD_SET( s, &fdWrite ); 
 
	// Select function set write timeout 
	DWORD dwBytesWritten = 0L; 
	int res = select( s+1, NULL, &fdWrite, NULL, pstTime ); 
	if ( res > 0) 
	{ 
		// Send message to peer or broadcast it 
		if (IsBroadcast() || IsSmartAddressing()) 
		{ 
			// use offset for Smart addressing 
			int nOffset = IsSmartAddressing() ? sizeof(SOCKADDR_IN) : 0; 
			if (IsSmartAddressing()) 
			{ 
				if ( dwCount < sizeof(SOCKADDR_IN))	// error - buffer to small 
				{ 
					SetLastError( ERROR_INVALID_USER_BUFFER ); 
					return -1L; 
				} 
 
				// read socket address from buffer 
				SockAddrIn sockAddr; 
				sockAddr.SetAddr((PSOCKADDR_IN) lpBuffer); 
 
				// Get Address and send data 
				if (sockAddr.sin_addr.s_addr != htonl(INADDR_BROADCAST)) 
				{ 
					LPSTR lpszData = (LPSTR)(lpBuffer + nOffset); 
					res = sendto( s, lpszData, dwCount-nOffset, 0, 
						(LPSOCKADDR)sockAddr, sockAddr.Size()); 
					dwBytesWritten = (DWORD)((res >= 0)?(res) : (-1)); 
					return dwBytesWritten; 
				} 
			} 
 
			// Broadcast send to all connected-peer 
			LockList(); // Lock this object addresses-list 
 
			CSockAddrList::iterator iter = m_AddrList.begin(); 
			for( ; iter != m_AddrList.end(); ) 
			{ 
				// Fix v1.3 - nOffset was missing 
				res = sendto( s, (LPCSTR)&lpBuffer[nOffset], dwCount-nOffset, 0, (LPSOCKADDR)(*iter), iter->Size()); 
				if (res < 0) 
				{ 
					CSockAddrList::iterator deladdr = iter; 
					++iter;	// get next 
					m_AddrList.erase( deladdr ); 
				} 
				else 
					++iter;	// get next 
			} 
 
			UnlockList(); // unlock this object addresses-list 
 
			// always return success - UDP 
			res = (int) dwCount - nOffset; 
		} 
		else // Send to peer-connection 
			res = send( s, (LPCSTR)lpBuffer, dwCount, 0); 
 
		dwBytesWritten = (DWORD)((res >= 0)?(res) : (-1)); 
	} 
 
	return dwBytesWritten; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// Run 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//		This function runs the main thread loop 
//		this implementation can be overloaded. 
//      This function calls CSocketComm::OnDataReceived() (Virtual Function) 
// PARAMETERS: 
// NOTES: 
//		You should not wait on the thread to end in this function or overloads 
/////////////////////////////////////////////////////////////////////////////// 
void CSocketComm::Run() 
{ 
	stMessageProxy stMsgProxy; 
	DWORD	dwBytes  = 0L; 
	DWORD	dwTimeout = DEFAULT_TIMEOUT; 
	LPBYTE  lpData  = (LPBYTE)&stMsgProxy; 
	DWORD	dwSize  = sizeof(stMsgProxy); 
 
	if (!IsSmartAddressing()) 
	{ 
		lpData = stMsgProxy.data; 
		dwSize = sizeof(stMsgProxy.data); 
	} 
 
	// Should we run as server mode 
	if (IsServer()) 
	{ 
		if (!IsBroadcast()) 
		{ 
			SOCKET sock = (SOCKET) m_hComm; 
			sock = WaitForConnection( sock ); 
 
			// Get new connection socket 
			if (sock != INVALID_SOCKET) 
			{ 
				ShutdownConnection( (SOCKET) m_hComm); 
				m_hComm = (HANDLE) sock; 
				OnEvent( EVT_CONSUCCESS ); // connect 
			} 
			else 
			{ 
				// Do not send event if we are closing 
				if (IsOpen()) 
					OnEvent( EVT_CONFAILURE ); // wait fail 
				return; 
			} 
		} 
	} 
	else 
		GetPeerName( stMsgProxy.address ); 
 
	while( IsOpen() ) 
	{ 
		// Blocking mode: Wait for event 
		dwBytes = ReadComm(lpData, dwSize, dwTimeout); 
 
		// Error? - need to signal error 
		if (dwBytes == (DWORD)-1L) 
		{ 
			// Do not send event if we are closing 
			if (IsOpen()) 
				OnEvent( EVT_CONDROP ); // lost connection 
 
			// special case for UDP, alert about the event but do not stop 
			if (IsBroadcast()) 
				continue; 
			else 
				break; 
		} 
 
		// Chars received? 
		if (IsSmartAddressing() && dwBytes == sizeof(SOCKADDR_IN)) 
			OnEvent( EVT_ZEROLENGTH ); 
		else if (dwBytes > 0L) 
		{ 
			OnDataReceived( lpData, dwBytes); 
		} 
 
		Sleep(0); 
	} 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// SocketThreadProc 
/////////////////////////////////////////////////////////////////////////////// 
// DESCRIPTION: 
//     Socket Thread function.  This function is the main thread for socket 
//     communication - Asynchronous mode. 
// PARAMETERS: 
//     LPVOID pParam : Thread parameter - a CSocketComm pointer 
// NOTES: 
/////////////////////////////////////////////////////////////////////////////// 
UINT WINAPI CSocketComm::SocketThreadProc(LPVOID pParam) 
{ 
	CSocketComm* pThis = reinterpret_cast( pParam ); 
	_ASSERTE( pThis != NULL ); 
 
	pThis->Run(); 
 
	return 1L; 
} // end SocketThreadProc