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);
}