I know how to manage TCP/IP Socket Client and Server and I understood everything but the problem is it can't handle a thousand client at daily basis, so I tried to learn IOCP and it seems that IOCP isn't really known everywhere, like I tried to search for it, it only shows the code for Server I tried to understand it but I can't.
When I tried to use IOCP for my server and normal tcp/ip or socket/send/recv for the client, It works well but It seems like it doesn't send all data and there's no way for me to know if the data is completed.
At first I thought it was a corrupted data but then I realize the problem is from the connection itself. If the server has alot of network traffic sometimes the data sent from client to server will be doubled in 1 WSARecv or sometimes its not completed at all, and now I tried to create IOCP for client but when I tried to send data to client using the server, the server workerthread will receive the data itself.
CLIENT WORKERTHREAD:
DWORD WINAPI CACGameClient::WorkerThread( LPVOID lpParam )
{
int nThreadId = (int)lpParam;
// this is the worker thread for client and it has only one thread
// so if the server crashed or disconnected I will just exit the thread
// and start the reconnection process which works.
// This is my first time so ask me anything you do not understand on my code.
DbgPrintSuccess( _X( "Thread #%d: Initialized" ), nThreadId );
DWORD dwBytesTransfered = NULL;
ULONG_PTR lpContext = NULL;
LPOVERLAPPED lpOverlapped = NULL;
CClientManager* pClientManager = NULL;
PPER_IO_CONTEXT lpIOContext = NULL;
WSABUF buffRecv;
WSABUF buffSend;
BOOL nRet = 0;
DWORD dwSendNumBytes = 0;
DWORD dwRecvNumBytes = 0;
while (TRUE)
{
BOOL bReturn = GetQueuedCompletionStatus( p_client->m_hCompletionPort, &dwBytesTransfered, &lpContext, &lpOverlapped, INFINITE );
if (!bReturn) {
DbgPrintError( _X( "Thread #%d: GetQueuedCompletionStatus failed. ( %d )" ), nThreadId, WSAGetLastError() );
break;
}
pClientManager = reinterpret_cast<CClientManager*>(lpContext);
if (!pClientManager) {
DbgPrintWarning( _X( "Thread #%d: lpContext is NULL." ), nThreadId );
break;
}
DWORD dwFlags = 0;
lpIOContext = pClientManager->pIOContext;
switch (lpIOContext->IOOperation)
{
case OP_READ:
{
lpIOContext->IOOperation = OP_WRITE;
lpIOContext->nTotalBytes = dwBytesTransfered;
lpIOContext->nSentBytes = 0;
lpIOContext->wsabuf.len = dwBytesTransfered;
nRet = WSASend( p_client->GetSocket(), &lpIOContext->wsabuf, 1,
&dwSendNumBytes, dwFlags, &(lpIOContext->Overlapped), NULL );
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())) {
DbgPrintError( "WSASend() failed: %d\n", WSAGetLastError() );
continue;
}
// AFTER I RECEIVE CODE FOR OP_WRITE AND WSARecv RUN IT WILL COME HERE
// THATS I CALL HERE THE DATA PROCESS
// ProcessData
break;
}
case OP_WRITE:
{
lpIOContext->IOOperation = OP_READ;
nRet = WSARecv( p_client->GetSocket(), &lpIOContext->wsabuf, 1,
&dwRecvNumBytes, &dwFlags, &lpIOContext->Overlapped, NULL );
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())) {
DbgPrintError( "WSARecv() failed: %d\n", WSAGetLastError() );
continue;
}
// I searched everywhere for the code and I found out they use OP_CODES
// now the problem here is where should I get the data?
// When WSARecv finished, it will send a new OP_READ code, right?
// BUT THEN WHY DOES IT GO BACK TO THIS THREAD RIGHT?
}
break;
}
}
std::thread( ReconnectThread, p_client ).detach();
DbgPrintWarning( _X( "Thread #%d: exited" ), nThreadId );
return 0;
}
THIS IS HOW I SEND MY DATA TO SERVER I COPIED THIS SOMEWHERE ON THE INTERNET
void InitializeContext( IO_OPERATION OpCode )
{
pIOContext = (PPER_IO_CONTEXT)malloc( sizeof( PER_IO_CONTEXT ) );
if (pIOContext)
{
pIOContext->Overlapped.Internal = 0;
pIOContext->Overlapped.InternalHigh = 0;
pIOContext->Overlapped.Offset = 0;
pIOContext->Overlapped.OffsetHigh = 0;
pIOContext->Overlapped.hEvent = NULL;
pIOContext->IOOperation = OpCode;
pIOContext->pIOContextForward = NULL;
pIOContext->nTotalBytes = 0;
pIOContext->nSentBytes = 0;
pIOContext->wsabuf.buf = pIOContext->Buffer;
pIOContext->wsabuf.len = sizeof( pIOContext->Buffer );
ZeroMemory( pIOContext->wsabuf.buf, pIOContext->wsabuf.len );
}
else
{
DbgPrintError( "HeapAlloc() PER_SOCKET_CONTEXT failed: %d\n", GetLastError() );
}
}
THIS IS WHERE WSASEND IS USED, AND ITS NOT FULL CODE HEHE BUT I GUESS U GOOD AND U WILL UNDERSTAND
if (u8SendBuffer)
{
delete pIOContext;
pIOContext = NULL;
InitializeContext( OP_WRITE );
pIOContext->wsabuf.buf = (LPSTR)&Info;
pIOContext->wsabuf.len = (ULONG)u8SendBufCnt;
pIOContext->nTotalBytes = u8SendBufCnt;
DWORD dwFlag = 0;
DWORD dwSend = 0;
int nResult = WSASend( GetSocket(), &pIOContext->wsabuf, 1, &dwSend, dwFlag, &(pIOContext->Overlapped), NULL);
int nLastEr = WSAGetLastError();
if (nResult == NULL)
{
if (nLastEr == ERROR_IO_PENDING )
{
DbgPrintSuccess( _X( "All buffers has been sent at once. ( %d )" ), dwSend );
}
else
{
DbgPrintError( _X( "All buffers failed to send at once. ( %d )" ), dwSend );
}
}
else
{
DbgPrintWarning( _X( "Sending unsent buffers, dwSent = %d, dwTotalSize = %d, Error = %d " ), dwSend, u8SendBufCnt, nLastEr );
pIOContext->nSentBytes += dwSend;
while (dwSend < u8SendBufCnt)
{
int remains = u8SendBufCnt - dwSend;
WSABUF BufRemains;
BufRemains.buf = u8SendBuffer + dwSend;
BufRemains.len = remains;
DWORD dwRemains = 0;
nResult = WSASend( GetSocket(), &BufRemains, 1, &dwRemains, dwFlag, &(pIOContext->Overlapped), NULL );
nLastEr = WSAGetLastError();
if (nResult == SOCKET_ERROR )
{
if (nLastEr == ERROR_IO_PENDING)
{
pIOContext->nSentBytes += dwRemains;
if (dwSend == u8SendBufCnt)
{
DbgPrintSuccess( _X( "All buffers has been sent. ( %d )" ), dwSend );
break;
}
}
else
{
DbgPrintError( _X( "All buffers failed to send. ( %d )" ), dwSend );
break;
}
}
}
}
free( u8SendBuffer );
}
THIS IS HOW I CONNECT MY CLIENT TO THE SERVER THE SERVER IS ALSO IOCP
if (WSAConnect( clientSocket, reinterpret_cast<sockaddr*>(&server_addr), sizeof( server_addr ), NULL, NULL, NULL, NULL ) != SOCKET_ERROR)
{
SetSocket( clientSocket );
DbgPrintInfo( _T( "Address: %s-%s" ), server_ip.c_str(), server_port.c_str() );
if (AssociateCompletionPort( m_hCompletionPort ) )
{
bRet = 0;
CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, NULL, NULL );
DWORD dwBytes = 0;
DWORD dwFlags = 0;
InitializeContext( OP_WRITE );
int nBytesRecv = WSASend( GetSocket(), &(pIOContext->wsabuf), 1,
&dwBytes, dwFlags, &(pIOContext->Overlapped), NULL );
int lastError = WSAGetLastError();
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != lastError))
{
DbgPrintError( "Error in Initial Post. (%d)", lastError );
}
}
else
{
WSACleanup();
bRet = 5;
}
}
Well I tried everything I can, so what I only know if after the client is accepted to the server it should do WSARecv for the initial post which I really don't understand why but it does work but sometimes it doesn't, it sometimes gives me 10054 WSAError, that was before I made my Client IOCP, after I convert my client to IOCP connection I added WSASend after the client connected to server and hoping it won't give me error 10054 again, but I haven't tested it since Im having problem of client and server receiving theirselves sent data
Example: Server send data to client -> Server WorkerThread Recv the data he sent for client, I mean why? how do I send it to client directly? and I do not understand how this IOCP works so I hope someone help me with this problem.