www.pudn.com > ChatUseIOCP.rar > DtServerSocketClient.cpp


#include "StdAfx.h" 
#include ".\dtServerSocketclient.h" 
 
Datatal::DtServerSocketClient::DtServerSocketClient(void) 
{ 
	m_nState = SSF_DEAD; 
	m_pfnDoneIORoutine = NULL; 
	m_sdListen = INVALID_SOCKET; 
	m_sdClient = INVALID_SOCKET; 
	m_pServer = NULL; 
	m_bPendingWrite = false; 
	m_bPendingRead = false; 
 
	m_wsaOutBuf.buf = new char[SERVER_WORK_BUFFER_SIZE]; 
	m_wsaOutBuf.len = 0; 
	memset(m_wsaOutBuf.buf, 0, SERVER_WORK_BUFFER_SIZE); 
 
	m_wsaInBuf.buf = new char[SERVER_WORK_BUFFER_SIZE]; 
	m_wsaInBuf.len = 0; 
	memset(m_wsaInBuf.buf, 0, SERVER_WORK_BUFFER_SIZE); 
 
	memset(&m_ovRead, 0, sizeof(OVERLAPPED_PLUS)); 
	memset(&m_ovWrite, 0, sizeof(OVERLAPPED_PLUS)); 
 
	m_ovWrite.pClient = (DtServerSocketClient*)this; 
	m_ovRead.pClient = (DtServerSocketClient*)this; 
 
	m_ovWrite.bCtxWrite = true; 
	m_ovRead.bCtxWrite = false; 
	m_nBufferPos = 0; 
} 
 
Datatal::DtServerSocketClient::~DtServerSocketClient(void) 
{ 
	delete []m_wsaOutBuf.buf; 
	delete []m_wsaInBuf.buf; 
} 
 
void Datatal::DtServerSocketClient::SetIoCompRoutine(LPOVERLAPPED_COMPLETION_ROUTINE IoFunc) 
{  
	m_pfnDoneIORoutine = IoFunc;  
} 
 
void Datatal::DtServerSocketClient::SetListenSocket(SOCKET sdListen)  
{  
	m_sdListen = sdListen;  
} 
 
void Datatal::DtServerSocketClient::InternalInit() 
{ 
 
	WriteLog(LP_LOW, GetClientId(), "Init", "Initing client..."); 
	if (!m_pServer) 
		throw DtException(SERVER_ERROR_GENERAL, "Init", "Server not set!"); 
	if (m_sdListen == INVALID_SOCKET) 
		throw DtException(SERVER_ERROR_GENERAL, "Init", "No listensocket have been defined"); 
	if (m_nState != SSF_DEAD) 
		throw DtServerException(WSAGetLastError(), "Init", "Client is not prepared for init."); 
 
	m_bPendingWrite = false; 
	m_bPendingRead = false; 
 
	m_wsaInBuf.len = 0; 
	memset(m_wsaInBuf.buf, 0, SERVER_WORK_BUFFER_SIZE); 
	m_wsaOutBuf.len = 0; 
	memset(m_wsaOutBuf.buf, 0, SERVER_WORK_BUFFER_SIZE); 
 
	//Create the  socket 
	DWORD dwFlags = WSA_FLAG_OVERLAPPED; 
	if (m_sdClient == INVALID_SOCKET) 
	{ 
		m_sdClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, NULL, WSA_FLAG_OVERLAPPED); 
		if (m_sdClient == INVALID_SOCKET) 
			throw DtServerException(WSAGetLastError(), "Connect failed", "WSASocket failed."); 
	} 
 
	DWORD dwBytesRecvd; 
	if (!AcceptEx(m_sdListen, 
		m_sdClient, 
		m_wsaInBuf.buf, 
		SERVER_WORK_BUFFER_SIZE-(sizeof(sockaddr_in)+16)*2, 
		sizeof(sockaddr_in) + 16, 
		sizeof(sockaddr_in) + 16, 
		&dwBytesRecvd, 
		&m_ovRead)) 
	{ 
		DWORD dwError = GetLastError(); 
		if (dwError != ERROR_IO_PENDING) 
		{ 
			if (dwBytesRecvd != 0) 
			{ 
 
			} 
			m_nState = SSF_DEAD; 
			OnError(dwError); 
			return; 
		} 
	} 
 
 
	WriteLog(LP_LOW, GetClientId(), "Init", "Inited."); 
	m_nState = SSF_ACCEPTING; 
	return; 
} 
 
 
 
void Datatal::DtServerSocketClient::HandleConnect() 
{ 
	DtLock aLock(m_cs);	//Lock the class, we do not want to do stuff 
 
	if (m_nState != SSF_ACCEPTING) 
		throw DtException(SERVER_ERROR_GENERAL, "HandleConnect", "Invalid mode, expected SSF_ACCEPTING."); 
 
 
	int locallen, remotelen; 
	sockaddr_in *plocal = 0, *premote = 0; 
 
	GetAcceptExSockaddrs(m_wsaInBuf.buf, 
		SERVER_WORK_BUFFER_SIZE-(sizeof(sockaddr_in)+16)*2, 
		sizeof(sockaddr_in) + 16, 
		sizeof(sockaddr_in) + 16, 
		(sockaddr **)&plocal, 
		&locallen, 
		(sockaddr **)&premote, 
		&remotelen); 
 
	memcpy(&m_LocalAddr, plocal, sizeof(sockaddr_in)); 
	memcpy(&m_RemoteAddr, premote, sizeof(sockaddr_in)); 
 
	//setsockopt( m_sdClient, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *) &m_sdListen, sizeof(m_sdListen) ); 
 
	//init sockaddr_in fields 
	if (BindIoCompletionCallback((HANDLE)m_sdClient, m_pfnDoneIORoutine, 0)) 
	{ 
		m_nState = SSF_CONNECTED; 
		char szDisplay[512], *pszTemp; 
		pszTemp = inet_ntoa( m_RemoteAddr.sin_addr ); 
		sprintf(szDisplay, "Connect from %s:%d", pszTemp, htons(m_RemoteAddr.sin_port)); 
		//if (pszTemp) delete [] pszTemp; 
		WriteLog(LP_NORMAL, GetClientId(), "Connect", szDisplay); 
	} 
	else 
	{ 
		throw DtException(GetLastError(), "HandleConnect", "BindIoCompletionCallback failed."); 
	} 
 
	m_bPendingRead = true; 
	m_ovRead.Internal = 0; 
	m_ovRead.InternalHigh = 0; 
	m_ovRead.Offset = 0; 
	m_ovRead.OffsetHigh = 0; 
} 
 
 
/// Enqueue stuff to our outgoing buffer. 
bool Datatal::DtServerSocketClient::Send(char* data, int nSize) 
{ 
	//Lock outbuffer 
	m_CritWrite.Lock(); 
	m_lOutBuffers.Append(data, (DWORD)nSize); 
	m_CritWrite.Unlock(); 
	WriteLog(Datatal::LP_NORMAL, GetClientId(), "Send", "Appending new outbuffer, size: %d", nSize); 
 
	//Trigger that we got a write operation 
	WriteOperation(); 
	return true; 
} 
 
bool Datatal::DtServerSocketClient::WriteOperation() 
{ 
	if (m_nState == SSF_DEAD || m_nState == SSF_SHUTTING_DOWN)  
	{ 
		WriteLog(LP_HIGH, GetClientId(), "Send", "Server is shutting down."); 
		return false; 
	} 
 
	//Lock outbound data 
	DtLock LockWrite(m_CritWrite); 
	DWORD dwBytesToWrite = 0, dwBytesWritten = 0, dwFlags = 0; 
 
	// Do we got a outbuffer? Are we finished sending it? 
	if (!m_lOutBuffers.pFirst && !m_lOutBuffers.pLast) 
	{ 
		WriteLog(Datatal::LP_NORMAL, GetClientId(), "Send", "No outbuffers."); 
		return false; 
	} 
 
	// workoutbuffer is large enough to handle the remainings of the current buffer. 
	Outbuffer* pBuffer = m_lOutBuffers.pFirst; 
	if (pBuffer->dwSize - m_nBufferPos <= SERVER_WORK_BUFFER_SIZE) 
	{ 
		memcpy(m_wsaOutBuf.buf, pBuffer->pBuffer + m_nBufferPos, pBuffer->dwSize - m_nBufferPos); 
		dwBytesToWrite = pBuffer->dwSize - m_nBufferPos; 
		WriteLog(Datatal::LP_NORMAL, GetClientId(), "Send", "Sent the buffer %d/%d.", m_nBufferPos + dwBytesToWrite, pBuffer->dwSize); 
		m_lOutBuffers.RemoveFirst(); 
		WriteLog(Datatal::LP_LOW, GetClientId(), "Send", "First/Last outbuffer: %X/%X", m_lOutBuffers.pFirst, m_lOutBuffers.pLast); 
		m_nBufferPos = 0; 
	} 
	else 
	{ 
		memcpy(m_wsaOutBuf.buf, pBuffer->pBuffer + m_nBufferPos, SERVER_WORK_BUFFER_SIZE); 
		m_nBufferPos += SERVER_WORK_BUFFER_SIZE; 
		dwBytesToWrite = SERVER_WORK_BUFFER_SIZE; 
		WriteLog(Datatal::LP_NORMAL, GetClientId(), "Send", "Sending part of buffer %d/%d", m_nBufferPos, pBuffer->dwSize); 
	} 
 
	m_wsaOutBuf.len = dwBytesToWrite; 
	WriteLog(Datatal::LP_LOW, GetClientId(), "Send", "WSASend"); 
	if (0 != WSASend(m_sdClient, &m_wsaOutBuf, 1, &dwBytesWritten, dwFlags, &m_ovWrite, NULL)) 
	{ 
 
		// Error? Check if it's a pending write. 
		int nLastError = WSAGetLastError(); 
		if (WSA_IO_PENDING != nLastError) 
		{ 
			WriteLog(LP_HIGH, GetClientId(), "Send", "Send ->Error occured while calling WriteFile(..): %d", nLastError); 
			OnError(nLastError); 
			return false; 
		} 
 
		m_bPendingWrite = true; 
		return true; 
	} 
	else 
	{ 
		return true; 
	} 
 
	if(!dwBytesWritten) 
	{ 
		WriteLog(LP_LOW, GetClientId(), "Send", "Send ->0 bytes written, disconnecting"); 
		OnError(WSAECONNRESET); 
		return false; 
	} 
 
	return false; 
} 
/* 
//Write operation. 
bool Datatal::DtServerSocketClient::WriteOperation() 
{ 
	if (m_nState == SSF_DEAD || m_nState == SSF_SHUTTING_DOWN)  
	{ 
		WriteLog(LP_HIGH, GetClientId(), "Send", "Server is shutting down."); 
		return false; 
	} 
 
	//Lock outbound data 
	DtLock LockWrite(m_CritWrite); 
 
	if (m_nOutBufSize > 0) 
	{ 
		DWORD dwBytesToWrite = 0, dwBytesWritten = 0, dwFlags = 0; 
 
		if (m_nOutBufSize > SERVER_WORK_BUFFER_SIZE) 
		{ 
			memcpy(m_wsaOutBuf.buf, m_szOutBuf, SERVER_WORK_BUFFER_SIZE); 
			memcpy(m_szOutBuf, m_szOutBuf + SERVER_WORK_BUFFER_SIZE, m_nOutBufSize - SERVER_WORK_BUFFER_SIZE); 
			dwBytesToWrite = SERVER_WORK_BUFFER_SIZE; 
			m_nOutBufSize -= SERVER_WORK_BUFFER_SIZE; 
		} 
		else 
		{ 
			memcpy(m_wsaOutBuf.buf, m_szOutBuf, m_nOutBufSize); 
			memset(m_szOutBuf, 0, SERVER_OUT_BUFFER_SIZE); 
			dwBytesToWrite = m_nOutBufSize; 
			m_nOutBufSize = 0; 
		}  
 
		m_wsaOutBuf.len = dwBytesToWrite; 
		WriteLog(LP_NORMAL, GetClientId(), "Send", "WSASend"); 
		if (0 != WSASend(m_sdClient, &m_wsaOutBuf, 1, &dwBytesWritten, dwFlags, &m_ovWrite, NULL)) 
		{ 
 
			// Error? Check if it's a pending write. 
			int nLastError = WSAGetLastError(); 
			if (WSA_IO_PENDING != nLastError) 
			{ 
				char szLog[256]; 
				sprintf(szLog, "Send ->Error occured while calling WriteFile(..): %d", nLastError); 
				WriteLog(LP_HIGH, GetClientId(), "Send", szLog); 
 
				OnError(nLastError); 
				return false; 
			} 
 
			m_bPendingWrite = true; 
 
			char szLog[256]; 
			sprintf(szLog, "Send ->Pending write %d bytes", dwBytesToWrite); 
			WriteLog(LP_LOW, GetClientId(), "Send", szLog); 
 
			return true; 
		} 
		else 
		{ 
			char szLog[256]; 
			sprintf(szLog, "Send ->Sent %d/%d bytes. (flags: %d)", dwBytesWritten, dwBytesWritten + m_nOutBufSize, dwFlags); 
			WriteLog(LP_LOW, GetClientId(), "Send", szLog); 
 
			return true; 
		} 
 
		if(!dwBytesWritten) 
		{ 
			char szLog[256]; 
			sprintf(szLog, "Send ->0 bytes written, disconnecting"); 
			WriteLog(LP_LOW, GetClientId(), "Send", szLog); 
 
			OnError(WSAECONNRESET); 
			return false; 
		} 
 
	}  
	return false; 
} 
*/ 
 
//CServerClientBase 
bool Datatal::DtServerSocketClient::ReadOperation(DWORD dwReadbytes) 
{ 
 
	//This is should be set on the first connection 
	if (m_nState == SSF_DEAD || m_nState == SSF_SHUTTING_DOWN)  
	{ 
		WriteLog(LP_NORMAL, GetClientId(), "Read", "Aborting, dead or shutting down..."); 
		return false; 
	} 
 
 
	//If we got no read operation, begin to read. 
	if (!m_bPendingRead) 
	{ 
		DWORD dwLastError = 0, dwBytesRecvd = 0, dwFlags = 0; 
		if (m_nState != SSF_CONNECTED) 
			return false; 
 
		WriteLog(LP_NORMAL, GetClientId(), "Read", "WSARecv"); 
 
#ifndef SERVER_USE_WSARECV 
		if (!ReadFile((HANDLE)m_sdClient, m_wsaInBuf.buf, SERVER_WORK_BUFFER_SIZE, &dwBytesRecvd, &m_ovRead)) 
		{ 
			dwLastError = GetLastError(); 
			if (ERROR_IO_PENDING != dwLastError) 
#else 
		if (0 != WSARecv(m_sdClient, &m_wsaInBuf, 1, &dwBytesRecvd,	&dwFlags, &m_ovRead, NULL) ) 
		{ 
			dwLastError = WSAGetLastError(); 
			if (WSA_IO_PENDING != dwLastError) 
#endif 
			{ 
				char szLog[256]; 
				sprintf(szLog, "Read -> Error occured while calling read function: %d", dwLastError); 
				WriteLog(LP_HIGH, GetClientId(), "Read", szLog); 
				OnError(dwLastError); 
				return false; 
			} 
 
			WriteLog(LP_LOW, GetClientId(), "Read", "Read -> Pending read inited"); 
			m_bPendingRead = true; 
			return false;	//return false since we gotto wait for io completion. 
		} 
 
		if(!dwBytesRecvd) 
		{ 
			WriteLog(LP_HIGH, GetClientId(), "Read", "Read -> 0 bytes recieved, disconnecting"); 
			OnError(WSAECONNRESET); 
			return false; 
		} 
 
		WriteLog(LP_LOW, GetClientId(), "Read", "Read -> completed directly, returning read bytes: %d", dwBytesRecvd); 
		m_bPendingRead = true; 
		return false; 
	} //!m_bPendingRead 
 
 
	if ( !dwReadbytes ) //Socket closed by Client 
	{ 
		DWORD dwFlags = 0; 
		if(!WSAGetOverlappedResult(m_sdClient, &m_ovRead, &dwReadbytes, FALSE, &dwFlags)) 
		{ 
			WriteLog(LP_HIGH, GetClientId(), "Read", "GetOverlappedResult() failed on Read! Bytes read is %d. Flags=%d.", dwReadbytes, dwFlags); 
			return false; 
		} 
 
		if (!dwReadbytes) 
		{ 
			WriteLog(LP_HIGH, GetClientId(), "Read", "0 bytes read!"); 
			OnError(WSAECONNRESET); 
			return false; 
		} 
 
	} 
	//else 
	{ 
		m_CritRead.Lock(); 
		OnReceive(m_wsaInBuf.buf, dwReadbytes); 
		m_bPendingRead = false; 
		m_CritRead.Unlock(); 
	} // if ( !dwReadbytes ) 
 
	return true; 
 
} 
 
 
void Datatal::DtServerSocketClient::Disconnect(bool bForce) 
{ 
	//already disconnecting. 
	DtLock aLock(m_cs); 
	if (m_nState == SSF_SHUTTING_DOWN || m_nState == SSF_DEAD) return; 
	m_nState = SSF_SHUTTING_DOWN; 
 
	WriteLog(LP_LOW, GetClientId(), "Disconnect", "Shutting down client..."); 
 
	struct linger li = { 0, 0 }; // default: SO_DONTLINGER 
	if ( bForce ) 
		li.l_onoff = 1; // SO_LINGER, timeout = 0 
	//else 
	//	shutdown(m_sdClient, SD_BOTH); 
 
	setsockopt( m_sdClient, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof li ); 
	closesocket( m_sdClient ); 
 
	m_CritWrite.Lock(); 
	Outbuffer* pBuffer = m_lOutBuffers.pFirst; 
	while (pBuffer) 
	{ 
		Outbuffer* pNext = m_lOutBuffers.pFirst->pNext; 
		delete[] pBuffer->pBuffer; 
		delete pBuffer; 
		Outbuffer* pBuffer = pNext; 
	} 
	m_lOutBuffers.pFirst = NULL; 
	m_lOutBuffers.pLast = NULL; 
	m_CritWrite.Unlock(); 
 
	m_sdClient = INVALID_SOCKET; 
 
	//Trigger event. 
	OnDisconnect(); 
 
	m_nState = SSF_DEAD; 
} 
 
void Datatal::DtServerSocketClient::OnError(DWORD dwErrorCode) 
{ 
	WriteLog(LP_HIGH, GetClientId(), "Error", "Restarting client due to error %d", dwErrorCode); 
	Disconnect(true); 
	Sleep(500); 
	Init(); 
} 
 
int Datatal::DtServerSocketClient::GetConnectTime() const 
{ 
	int nSecs; 
	int nBytes = sizeof(nSecs); 
	int nErr = getsockopt( m_sdListen, SOL_SOCKET, SO_CONNECT_TIME,	(char *)&nSecs, &nBytes ); 
	if ( nErr != NO_ERROR )  
	{ 
		char szLog[128]; 
		sprintf(szLog, "getsockopt(SO_CONNECT_TIME) failed: %ld", WSAGetLastError()); 
		WriteLog(LP_NORMAL, GetClientId(), "misc", szLog); 
		return -1; 
	} 
	return nSecs; 
} 
 
void Datatal::DtServerSocketClient::OnWriteLog(int nPrio, int nClientId, const char* pszCategory, const char* pszString) const 
{ 
	m_pServer->OnWriteLog(nPrio, nClientId, pszCategory, pszString); 
} 
 
void Datatal::DtServerSocketClient::WriteLog(int nPrio, int nClientId, const char* pszCategory, const char* pszString, ...) const 
{ 
	char		szFormattedString[1024]; 
	va_list		argptr; 
 
	//Initialize  
	memset(szFormattedString, 0, sizeof(szFormattedString)); 
	va_start(argptr, pszString); 
	vsprintf(szFormattedString, pszString, argptr); 
	va_end(argptr); 
 
	m_pServer->OnWriteLog(nPrio, nClientId, pszCategory, szFormattedString); 
}