IO Completion Port Initial Read and Bi-Directional Data - c++

I have the following simplified IO Completion Port server C++ code:
int main(..)
{
startCompletionPortThreadProc();
// Await client connection
sockaddr_in clientAddress;
int clientAddressSize = sizeof( clientAddress );
SOCKET acceptSocket = WSAAccept( serverSocket, (SOCKADDR*)&clientAddress, &clientAddressSize, NULL, NULL);
// Connected
CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, 0, 0 );
// Issue initial read
read( acceptSocket );
}
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
while( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
if( WaitForSingleObject( exitEvent, 0 ) == WAIT_OBJECT_0 )
{
break;
}
if( !perIoData )
continue;
if( bytesTransferred == 0 )
{
//TODO
}
switch( perIoData->operation )
{
case OPERATION_READ:
{
// Bytes have been received
if( bytesTransferred < perIoData->WSABuf.len )
{
// Terminate string
perIoData->WSABuf.buf[bytesTransferred] = '\0';
perIoData->WSABuf.buf[bytesTransferred+1] = '\0';
}
// Add data to message build
message += std::tstring( (TCHAR*)perIoData->WSABuf.buf );
// Perform next read
perIoData->WSABuf.len = sizeof( perIoData->inOutBuffer );
perIoData->flags = 0;
if( WSARecv( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesTransferred, &( perIoData->flags ), &( perIoData->overlapped ), NULL ) == 0 )
{
// Part message
continue;
}
if( WSAGetLastError() == WSA_IO_PENDING )
{
// End of message
//TODO: Process message here
continue;
}
}
}
break;
case OPERATION_WRITE:
{
perIoData->bytesSent += bytesTransferred;
if( perIoData->bytesSent < perIoData->bytesToSend )
{
perIoData->WSABuf.buf = (char*)&( perIoData->inOutBuffer[perIoData->bytesSent] );
perIoData->WSABuf.len = ( perIoData->bytesToSend - perIoData->bytesSent);
}
else
{
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = _tcslen( perIoData->inOutBuffer ) * sizeof( TCHAR );
perIoData->bytesSent = 0;
perIoData->bytesToSend = perIoData->WSABuf.len;
}
if( perIoData->bytesToSend )
{
if( WSASend( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesTransferred, 0, &( perIoData->overlapped ), NULL ) == 0 )
continue;
if( WSAGetLastError() == WSA_IO_PENDING )
continue;
}
}
break;
}
}
return 0;
}
bool SocketServer::read( SOCKET socket, HANDLE completionPort )
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory( perIoData, sizeof( PER_IO_DATA ) );
perIoData->socket = socket;
perIoData->operation = OPERATION_READ;
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = sizeof( perIoData->inOutBuffer );
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesReceived = 0;
if( WSARecv( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesReceived, &( perIoData->flags ), &( perIoData->overlapped ), NULL ) == SOCKET_ERROR )
{
int gle = WSAGetLastError();
if( WSAGetLastError() != WSA_IO_PENDING )
{
delete perIoData;
return false;
}
}
return true;
}
bool SocketServer::write( SOCKET socket, std::tstring& data )
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory( perIoData, sizeof( PER_IO_DATA ) );
perIoData->socket = socket;
perIoData->operation = OPERATION_WRITE;
perIoData->WSABuf.buf = (char*)data.c_str();
perIoData->WSABuf.len = _tcslen( data.c_str() ) * sizeof( TCHAR );
perIoData->bytesToSend = perIoData->WSABuf.len;
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesSent = 0;
if( WSASend( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesSent, 0, &( perIoData->overlapped ), NULL ) == SOCKET_ERROR )
{
if( WSAGetLastError() != WSA_IO_PENDING )
{
delete perIoData;
return false;
}
}
return true;
}
1) The first issue I have is with the initial read.
On client connection (accept), I issue a read. As the client hasn't sent any data yet, WSAGetLastError() is WSA_IO_PENDING and the read method returns.
When the client then sends data, the thread remains stuck in the GetQueuedCompletionStatus call (as I assume I need another WSARecv call?).
Am I supposed to keep looping the read method until data arrives? That doesn't seem logical, I thought by issuing the initial read GetQueuedCompletionStatus would complete when data arrived.
2) I need to read and write data bi-directional without acknowledgements. Therefore I've also created a client with the IOCP thread. Is it actually possible to do this with completion ports or does a read have to be followed by a write?
Sorry for what feels like basic questions, but after trawling the internet and building IOCP examples I'm still unable to answer the questions.
Many thanks in advance.

On client connection (accept), I issue a read. As the client hasn't sent any data yet, WSAGetLastError() is WSA_IO_PENDING and the read method returns.
That is normal behavior.
When the client then sends data, the thread remains stuck in the GetQueuedCompletionStatus call (as I assume I need another WSARecv call?).
No, you do not need another call. And if it is getting stuck, then you are not associating the read with the I/O Completion Port correctly.
Am I supposed to keep looping the read method until data arrives?
No. You need to call WSARecv() one time for the initial read. The WSA_IO_PENDING error means the read is waiting for data and will signal the I/O Completion Port when data actually arrives. DO NOT call WSARecv() (or any other read function) until that signal actually arrives. Then you can call WSARecv() again to wait for more data. Repeat until the socket is disconnected.
I thought by issuing the initial read GetQueuedCompletionStatus would complete when data arrived.
That is exactly what is supposed to happen.
2) I need to read and write data bi-directional without acknowledgements. Therefore I've also created a client with the IOCP thread. Is it actually possible to do this with completion ports
Yes. Reading and writing are separate operations, they are not dependent on each other.
does a read have to be followed by a write?
Not if your protocol does not require it, no.
Now, with that said, there are some issues with your code.
On a minor note, WSAAccept() is synchronous, you should consider using AcceptEx() instead so it can use the same I/O Completion Port for reporting new connections.
But more importantly, when a pending I/O operation fails, GetQueuedCompletionStatus() returns FALSE, the returned LPOVERLAPPED pointer will be non-NULL, and GetLastError() will report why the I/O operation failed. However, if GetQueuedCompletionStatus() itself fails, the returned LPOVERLAPPED pointer will be NULL, and GetLastError() will report why GetQueuedCompletionStatus() failed. This difference is clearly explained in the documentation, but your while loop is not accounting for it. Use a do..while loop instead and act according to the LPOVERLAPPED pointer:
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
do
{
if( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
// I/O success, handle perIoData based on completionKey as needed...
}
else if( perIoData )
{
// I/O failed, handle perIoData based on completionKey as needed...
}
else
{
// GetQueuedCompletionStatus() failure...
break;
}
}
while( WaitForSingleObject( exitEvent, 0 ) == WAIT_TIMEOUT );
return 0;
}
On a side note, instead of using an event object to signal when completionPortThreadProc() should exit, consider using PostQueuedCompletionionStatus() instead to post a termination completionKey to the I/O Completion Port, then your loop can look for that value:
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
do
{
if( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
if( completionKey == MyTerminateKey )
break;
if( completionKey == MySocketIOKey )
{
// I/O success, handle perIoData as needed...
}
}
else if( perIoData )
{
// I/O failed, handle perIoData based on completionKey as needed...
}
else
{
// GetQueuedCompletionStatus() failure...
break;
}
}
while( true );
return 0;
}
CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, MySocketIOKey, 0 );
PostQueuedCompletionStatus( completionPort, 0, MyTerminateKey, NULL );

Related

How to monitor whether a ZeroMQ server exists?

I want to check the existence ( state ) of a server before I send a ZeroMQ request, but I have no idea how to do it.
Q : I want to check the existence ( state ) of a server before I send a ZeroMQ request
The solution is to setup and use the services of a zmq_socket_monitor()
// Read one event off the monitor socket; return value and address
// by reference, if not null, and event number by value. Returns -1
// in case of error.
static int
get_monitor_event ( void *monitor,
int *value,
char **address
)
{
zmq_msg_t msg;
zmq_msg_init ( &msg ); // First frame in message contains event number and value
if ( zmq_msg_recv ( &msg, monitor, 0 ) == -1 ) return -1; // Interrupted, presumably
assert ( zmq_msg_more ( &msg ) & "REASON: Frame #1 FAILED TO SIG 2nd, EXPECTED, FRAME TO COME" );
uint8_t *data = ( uint8_t * ) zmq_msg_data ( &msg );
uint16_t event = *( uint16_t * ) ( data );
if ( value )
*value = *( uint32_t * ) ( data + 2 );
zmq_msg_init ( &msg ); // Second frame in message contains event address
if ( zmq_msg_recv ( &msg, monitor, 0 ) == -1 ) return -1; // Interrupted, presumably
assert ( !zmq_msg_more ( &msg ) & "REASON: Frame #2 FAILED TO SIG more, NOT EXPECTED, FRAMEs TO COME" );
if ( address ) {
uint8_t *data = ( uint8_t * ) zmq_msg_data ( &msg );
size_t size = zmq_msg_size ( &msg );
*address = ( char * ) malloc ( size + 1 );
memcpy ( *address, data, size );
( *address )[size] = 0;
}
return event;
}
int main ( void )
{
void *ctx = zmq_ctx_new ();
assert ( ctx & "REASON: Context FAILED to instantiate" );
void *client = zmq_socket ( ctx, ZMQ_DEALER );
assert ( client & "REASON: Socket FAILED to instantiate" );
// Socket monitoring only works over inproc://
int rc = zmq_socket_monitor ( client, "inproc://monitor-client-side", ZMQ_EVENT_ALL );
assert ( rc == 0 & "REASON: socket_monitor FAILED to instantiate over INPROC:// transport-class" );
// Create socket for collecting monitor events
void *client_side_mon = zmq_socket ( ctx, ZMQ_PAIR );
assert ( client_side_mon & "REASON: socket_monitor receiving Socket FAILED to instantiate " );
// Connect these to the inproc endpoints so they'll get events
rc = zmq_connect ( client_side_mon, "inproc://monitor-client-side" );
assert ( rc == 0 & "REASON: .connect()-method FAILED to get connected" );
// Now do whatever you need
...
// Close client
close_zero_linger ( client );
// --------------------------------------------------------------------
// How to collect and check events from socket_monitor:
int event = get_monitor_event ( client_side_mon, NULL, NULL );
if ( event == ZMQ_EVENT_CONNECT_DELAYED )
event = get_monitor_event ( client_side_mon, NULL, NULL );
assert ( event == ZMQ_EVENT_CONNECTED & "REASON: [client]-socket still not in an expected, .connect()-ed, state" );
...
...
event = get_monitor_event ( client_side_mon, NULL, NULL );
assert ( event == ZMQ_EVENT_MONITOR_STOPPED & "REASON: [client]-socket not in an expected, .close()-ed, state" );
// --------------------------------------------------------------------
// FINALLY:
// --------------------------------------------------------------------
// Close down the sockets
close_zero_linger ( client_side_mon );
zmq_ctx_term ( ctx );
return 0;
}
( included in API since v3.2+ )
You are best off setting up a full connection and devising a simple ACK protocol between the client and server before the sockets is considered to be working normally. If the client receives the ACK within a reasonable time the server is up. Otherwise the server is down, and the client is best closing the socket and trying again until it succeeds.
N.B. If the socket isn't closed, the messages can build up in the ZMQ send queue and risk flooding the server with lots of ACK messages when the server does finally connect.

WINSOCK2 WSAAsyncSelect deprecated issue

Hi I am using winsock2 and I am trying to get async communication.
I tried with TCP server msdn which is waiting for accept.
I tried with WSAAsyncSelect before the listen function.
WSAAsyncSelect(ListenSocket,
m_hWnd,
WM_SOCKET,
(FD_CLOSE | FD_ACCEPT | FD_READ));
and is showing the following error ..
Error C4996 'WSAAsyncSelect': Use WSAEventSelect() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
How can I solve this .. Thanks in advance ..
To get rid of that warning you can define #define _WINSOCK_DEPRECATED_NO_WARNINGS
at the top of your source file, which will disable there types of warnings.
That said, it might be worth listening to this warning and using WSAEventSelect instead.
Since WSAEventSelect signals an event instead of posting a message to the Window's message queue which is what WSAAsyncSelect does. Posting into the message queue is slower and adds additional processing that isn't really needed.
I have an example laying around which i'll post here for you:
... I may have gone overboard... but it's all useful and relevant.
Setup Listening Socket
if ( socket_name == INVALID_SOCKET )
{
P_ERR( "Could not create socket ( Id: %d ): %d\n", id, WSAGetLastError() );
return INVALID_SOCKET;
}
rerror = bind( socket_name, (SOCKADDR*)&sock_addr, sizeof( sock_addr ) );
if ( rerror != SOCKET_ERROR )
{
rerror = listen( socket_name, MAX_LISTEN_QUEUE );
if ( rerror != SOCKET_ERROR )
{
/* Selects the events that will trigger the `socket_event` windows event. */
/* socket_event was created using 'CreateEvent( NULL, TRUE, FALSE, NULL );' */
WSAEventSelect( socket_name, socket_event, FD_ALL_EVENTS );
if ( !startStatusThread() )
{
rerror = 1;
P_ERR( "Status thread failed: %d\n", id );
}
}
else
{
P_ERR( "listen() error %d : Error %d\n", id, WSAGetLastError() );
closesocket( socket_name );
}
}
else
{
P_ERR( "bind() error ( Id: %d ): %d\n", id, WSAGetLastError() );
closesocket( socket_name );
}
Process Events From Socket
/* waits 10ms for events in the event array ( in this case just 1 event as socket_event ). */
rerror = WSAWaitForMultipleEvents( 1, &socket_event, FALSE, 10, FALSE );
if ( rerror == WSA_WAIT_TIMEOUT )
{
continue; /* this block of code runs in a while loop. */
}
index = rerror - WSA_WAIT_EVENT_0; /* get the smallest index of a triggered event */
if ( rerror != WSA_WAIT_TIMEOUT && rerror != WSA_WAIT_FAILED )
{
/* returns a list of the events that occured. */
rerror = WSAEnumNetworkEvents( socket_name, socket_event, &events );
if ( rerror == SOCKET_ERROR )
{
P_ERR( "WSAEnumNetworkEvents Error %d: Id: %d\n", WSAGetLastError(), pThis->id );
continue;
}
/* look below for this function. */
handleNetworkEvents( events, index );
}
Handling the Events.
void
handleNetworkEvents( WSANETWORKEVENTS e, const int socket_index )
{
int rerror = 0;
/* on accept. */
if ( e.lNetworkEvents & FD_ACCEPT )
{
if ( e.iErrorCode[FD_ACCEPT_BIT] == 0 )
{
onAccept();
}
else
{
P_ERR( "Unknown network event error %d\n", id );
}
}
/* on connect */
if ( e.lNetworkEvents & FD_CONNECT )
{
if ( e.iErrorCode[FD_CONNECT_BIT] == 0 )
{
sendRead(); /* send first read request */
}
else
{
P_ERR( "Unknown network event error %d\n", id );
}
}
/* on successful read */
if ( e.lNetworkEvents & FD_READ )
{
sendRead(); /* get read data and queue another request. */
callback( id, inBuffer.buf, lastReadSize ); /* process data. */
}
/* on close. */
if ( e.lNetworkEvents & FD_CLOSE )
{
/* close the current event and make a new one ready for a new connection. */
onClose( socket_index );
}
}

C++ Socket Connect Issue

I have a socket connect function, the issue is that if the client is started before the server, the connection shows as connected, but for some reason returns fails. I am not sure where the failure is and would really appreciate any help:
The function is:
bool IPV4Socket::Connect( std::string hostname
, unsigned short remotePort
, TimeoutValue *timeout )
{
AddrInfo getResults;
AddrInfo getaddrinfoHints;
int connReturn = 0;
SockAddr_In *addrData;
bool connectSuccess = false;
std::string service = std::to_string( remotePort );
getaddrinfoHints.ai_family = AddressFamily_inet;
getaddrinfoHints.ai_socktype = SockType_stream;
if ( m_socketAdaptor->getaddrinfo( hostname
, service
, &getaddrinfoHints
, &getResults ) != 0 )
{
return false;
}
addrData = (SockAddr_In *)&( *getResults.ai_addr.begin() );
connReturn = m_socketAdaptor->connect( m_socket
, (const Sockaddr *)addrData
, (int)getResults.ai_addrlen );
if ( connReturn == SocketError)
{
int m_lastErrorCode = m_socketAdaptor->GetLastError();
// Connection error : FATAL
if ( ( m_lastErrorCode != SockErr_EWOULDBLOCK) &&
( m_lastErrorCode != SockErr_EALREADY ) )
{
connectSuccess = false;
}
else
{
SocketSet writeFDS;
SocketSet exceptFDS;
int selectReturn = 0;
// Clear all the socket FDS structures
SocketSet_ZERO( &writeFDS );
SocketSet_ZERO( &exceptFDS );
// Put the socket into the FDS structures
SocketSet_SET( m_socket, &writeFDS );
SocketSet_SET( m_socket, &exceptFDS );
selectReturn = m_socketAdaptor->select( -1
, NULL
, &writeFDS
, &exceptFDS
, timeout );
if ( selectReturn == SocketError )
{
// Any errors are bad
connectSuccess = false;
}
else if ( selectReturn > 0 )
{
// Check for error (exception) first
if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &exceptFDS ) )
{
connectSuccess = false;
}
else if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &writeFDS ) )
{
// Select returned 'writable', we're connected!
connectSuccess = true;
m_isConnected = true;
}
}
}
}
else
{
connectSuccess = true;
m_isConnected = true;
}
return connectSuccess;
}
I am not sure if I am missing the point, or if I have overly complicated the function.
Helllp :)
Notes:
* By the way, m_socketAdaptor-> functions are just wrappers.
* If you start server and then client, it works...
You can't reconnect a socket which has already failed to connect. You have to close it and create a new socket. Therefore that should be done in the connect method, not wherever it is done now.

COM + WaitForSingleObject

I've been trying to find a good architecture for one application for the last few days, and after some research I'm finally stuck, and the reason is COM.
The app in question will have multiple GUI threads, and they will schedule work items for worker thread. The worker thread will initialize COM via CoInitialize(NULL);, create few COM components and will go into a loop which will wait for WaitForMultipleObjects(2, ...) (ExitEvent - to indicate that app is shutting down and ManualResetEvent - to indicate that there are actually work items to process), and on successful wait, will process the items and PostMessage them back to GUI threads. The ManualResetEvent will be reset inside worker if the queue will be empty and will happen inside queue critical section.
The problem is that COM, as usual, makes EVERYTHING 1000x harder...
If I understand correctly, CoInitialize(NULL); creates a hidden window, and any message posted during WaitForSingle/MultipleObject/s can cause a deadlock.
So, I need to call the MsgWaitForMultiple objects. Which in turn can fail if messages are not pumped correctly. Unfortunately, I can't quite understand how to pump them in a correct way. Do I have to create my own message loop? Will the app crash if COM decides to create a messagebox?
So far it seems I have to proceed like this?
HANDLE hEvents[2] = {};
int ThreadProc(LPVOID lpParam) {
int nRetVal = 0;
CoInitialize(NULL);
CComPtr<ISomething> smthn;
smthn.CoCreateInstance(...);
MSG msg = {};
bool bRun = true;
while(bRun) {
while(PeekMessage(&msg, ??NULL/-1??, 0, 0, PM_REMOVE)) { /*Which one here?*/
if(msg.Message == WM_QUIT) {
bRun = false;
nRetVal = msg.wParam;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(MsgWaitForMultipleObjects(2, &hEvents, ...)) {
if(exitevent) { bRun = false; nRetVal = 0; }
else if(processevent) { [processdata] }
}
}
smthn.release();
CoUninitialize();
return nRetVal;
}
But what about hidden window, messageboxes, am I even on the right path?
Just use CoWaitForMultipleHandles and it will do the necessary message pumping on the hidden COM window for inter-thread syncing.
The hidden window is of class OleMainThreadWndClass with OleMainThreadWndName as caption but on win9x its class is WIN95 RPC Wmsg. It's hidden which means you can't use straight EnumThreadWindows to find it.
Seems like overkill, but this worked for me :
int waitAndDispatch( HANDLE* event_handle, unsigned int ev_count, DWORD timeout )
{
int rval = -1;
bool bLoop = true; // if the loop should terminate
HANDLE* pHList = new HANDLE[ev_count];
for( unsigned int i = 0; i < ev_count; ++i )
{
pHList[i] = event_handle[i];
}
while( bLoop )
{
DWORD res = ::MsgWaitForMultipleObjects( ev_count, pHList, false, timeout, QS_ALLPOSTMESSAGE | QS_SENDMESSAGE );
if( res == WAIT_OBJECT_0 + ev_count ) // messages arrived
{
MSG tmpMsg;
bool hasMsg = true;
while( bLoop && hasMsg )
{
::PeekMessage( &tmpMsg, 0, 0, 0, PM_NOREMOVE );
if( ::PeekMessage( &tmpMsg, 0, WM_USER, WM_USER, PM_REMOVE ) || // WM_USER for COM
::PeekMessage( &tmpMsg, 0, 0, WM_KEYFIRST - 1, PM_REMOVE ) // all destroy update, ...
)
{
DWORD val = ::WaitForMultipleObjects( ev_count, pHList, false, 0 );
if( val >= WAIT_OBJECT_0 && val <= (WAIT_OBJECT_0 + ev_count) )
{
rval = val - WAIT_OBJECT_0;
bLoop = false;
}
::DispatchMessage( &tmpMsg );
}
else
{
hasMsg = false;
}
}
}
else if( res >= WAIT_OBJECT_0 && res < (WAIT_OBJECT_0 + ev_count) )
{
rval = res - WAIT_OBJECT_0;
bLoop = false;
}
else if( res == WAIT_TIMEOUT )
{
rval = ev_count;
bLoop = false;
}
else
{
rval = -1;
bLoop = false;
}
}
delete[] pHList;
return rval;
}
I had to write this piece for VB6 and its thread interactions over com compartments ... .
If you initialize your thread apartment with CoInitializeEx( 0, COINIT_MULTITHREADED ), your COM calls will not be queued into an message queue. But then you have the problem of objects created in different COM apartments. These need to be marshaled ... .

I was making this program and the server wont send to the client

void CApplication::SendData( const char pBuffer[] )
{
if( pBuffer == NULL )
{
Log()->Write( ELogMessageType_ERROR, "Cannot send NULL message.");
return;
}
// calculate the size of that data
unsigned long messageSize = strlen( pBuffer );
// fix our byte ordering
messageSize = htonl( messageSize );
if( isServer == true )
{
for( unsigned int i = ESocket_CLIENT0; i < ESocket_MAX; ++i )
{
// send the message size
if( m_Socket[ i ] > 0 )
{
if( send( m_Socket[ i ], (char*)&messageSize, sizeof( messageSize ), 0 ) == SOCKET_ERROR )
{
Log()->Write( ELogMessageType_ERROR, "[Application] Send error: %i to socket %i", WSAGetLastError(), m_Socket[ i ] );
continue;
}
// fix our message size back to host ordering
messageSize = ntohl(messageSize);
// send the actual message
if( send( m_Socket[ i ], pBuffer, messageSize, 0 ) == SOCKET_ERROR )
{
Log()->Write( ELogMessageType_ERROR, "[Application] Send error: %i to socket %i", WSAGetLastError(), m_Socket[ i ] );
continue;
}
Log()->Write( ELogMessageType_MESSAGE, "[Application] SEND: %s", pBuffer );
}
}
}
You're not handling the case where send() sends less data than you've asked it to. You need to loop if that is the case, until all data has gone out. You're also not handling errors in general, if a client has disconnected, send() might return -1 for instance.
The typical approach is something like::
for(size_t to_go = messageSize; to_go > 0;)
{
int put = send(sock, buf, to_go);
if(put < 0)
{
perror("Socket send() error");
break;
}
buf += put;
to_go -= put;
}
This attempts to send the entire remaining message, until all of it has been sent. You will of course need to adapt for your specific variable names, do better error-handling, and so on; please view the above as a sketch.