EAGAIN in ZMQ extended request reply - c++

I'm trying to create a REQ <--> Router <--> Dealer <--> REP communication in C++. The child process binds the router and dealer, proxies between router and dealer, connects the REP to the dealer and waits for a message with zmq_recv.
The parent process connects a REQ to the router and tries to send a message, however I'm getting a zmq_send error in parent: Resource temporarily unavailable (which is EAGAIN). According to zmq_send docs, EAGAIN means:
Non-blocking mode was requested and the message cannot be sent at the moment.
However the message does get sent since it is received in the child process. Why does it return that errno?
Here is the MCVE:
#include <zmq.h>
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <thread>
#include <stdio.h>
int main() {
char connect_path[35];
int rc;
int msg;
pid_t child_pid = fork();
if (child_pid == 0) {
// Child
void* child_context = zmq_ctx_new ();
if (child_context == NULL) {
std::cerr << "\nChild context error\n";
}
void* router = zmq_socket(child_context, ZMQ_ROUTER);
if (router == NULL) {
perror("zmq_socket of type router error");
}
char bind_path[35];
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-router", getpid());
rc = zmq_bind(router, bind_path);
assert (rc == 0);
void* dealer = zmq_socket(child_context, ZMQ_DEALER);
if (dealer == NULL) {
perror("zmq_socket of type dealer error");
}
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_bind(dealer, bind_path);
assert (rc == 0);
std::thread z_proxy (zmq_proxy, router, dealer, nullptr);
z_proxy.detach();
void* rep_socket = zmq_socket (child_context, ZMQ_REP);
if (rep_socket == NULL) {
perror("zmq_socket of type rep error");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_connect(rep_socket, connect_path);
assert (rc == 0);
while(1) {
if (zmq_recv (rep_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_recv error");
}
printf("\nReceived msg %d in process %d\n", msg, getpid());
break;
}
if (zmq_close(rep_socket) != 0) {
perror("zmq_close of rep_socket in child error");
}
if (zmq_ctx_term(child_context) != 0) {
perror("zmq_ctx_term of child_context error");
}
} else {
// Parent
sleep(1);
void* parent_context = zmq_ctx_new ();
if (parent_context == NULL) {
std::cerr << "\nParent ctx error\n";
}
void* req_socket = zmq_socket (parent_context, ZMQ_REQ);
if (req_socket == NULL) {
perror("zmq_socket of type req error in parent");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-router", child_pid);
rc = zmq_connect(req_socket, connect_path);
assert (rc == 0);
msg = 30;
if (zmq_send (req_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_send error in parent");
}
if (zmq_close(req_socket) != 0) {
perror("zmq_close of req_socket in parent error");
}
if (zmq_ctx_term(parent_context) != 0) {
perror("zmq_ctx_term of parent_context error");
}
}
}

Step 1: Make a trivial test:
Well, as a minimum point, there ought be this sort of test-en-Queueing first:
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d\nZMQ: zmq_errno ~ %s\n",
rc,
zmq_strerror ( zmq_errno() )
);
.
Step 2: post the printed outputs
Next, if there are any "missed" shots, the error-analysis may advise on potential reason(s)
( if and only if the parent_ctx indeed rejected to even accept the data from a simplest ever zmq_send() call into it's internal queueing facility with an explicit reason for having done so ).
Otherwise we know nothing ( and the ZMQ_DONTWAIT flag is not the reason here ).
As the test was run, it yielded:
INF: zmq_send ( req_socket, 'A_TEST_BLOCK', 12, ZMQ_DONTWAIT )
ZMQ: returned rc == 12
ZMQ: zmq_errno ~ Resource temporarily unavailable
Step 3:
The test has confirmed, as per documentation:
The zmq_send() function shall return number of bytes in the message if successful.
So, let's dig a step deeper:
int major, minor, patch;
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch,
zmq_strerror ( zmq_errno() )
);
Step 4:
In case the bleeding-edge API-updates do not conform to the published API-specification, document the incident:
printf ( "EXPECT( NO ERROR, ON START ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( <major>.<minor>.<patch> ): zmq_version ~\n" );
int major, minor, patch
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch
)
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( NO ERROR ): zmq_send() ~ %s\n" );
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d which ouhgt be == 12, is it?\n",
rc
);
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
and feel free to file an issue, if unexpected results appear.

Related

IO Completion Port Initial Read and Bi-Directional Data

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

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.

Server/Client TCP asynchronous (winsock) // FD_WRITE issue

I need your help because I have to make two console application in C++ : a client able to send as many string as possible to a server (in order to send coordinates). I succeeded to make a blocking socket but as I have to integrate it after in a development platform (3D VIA Virtools) which call my script at each frame, I have no other solution than using asynchronous sockets.
*My problem is that I can only send string once, and after I don't receive FD_WRITE anymore...*
This begin to drive me crazy so any help will be highly appreciated (I'm a beginner in programming), thanks in advance to everyone who will feel a little bit concerned by my problem
Here is my code,
Server
#include <winsock2.h>
#include <Windows.h>
#include <conio.h>
#pragma comment(lib, "ws2_32.lib")
#define SOCKET_ERRNO WSAGetLastError()
#define ADDRESS "127.0.0.1"
#define PORT 1234
static SOCKET ListenFirstFreePort()
{
struct sockaddr_in addr;
int len = sizeof(addr);
SOCKET hSocket;
// Create socket
hSocket = socket( PF_INET, SOCK_STREAM, 0 );
if( hSocket == INVALID_SOCKET )
{
printf( "socket() error %d\n", SOCKET_ERRNO );
exit(1);
}
// Connexion setting for local connexion
addr.sin_family = AF_INET ;
addr.sin_addr.s_addr = inet_addr(ADDRESS);
addr.sin_port = htons (PORT);
// bind socket
if ( bind( hSocket, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR )
{
printf( "bind() error %d\n", SOCKET_ERRNO );
exit(1);
}
// listen
if ( listen( hSocket, 100) == SOCKET_ERROR )
{
printf( "listen() error %d\n", SOCKET_ERRNO );
exit(1);
}
return hSocket;
}
void main()
{
WSADATA stack_info;
SOCKET ahSocket[2];
WSAEVENT ahEvents[2];
DWORD dwEvent;
WSANETWORKEVENTS NetworkEvents;
int rc;
// Initialize Winsock
WSAStartup(MAKEWORD(2,0), &stack_info) ;
// Create events
ahEvents[0] = WSACreateEvent();
ahEvents[1] = WSACreateEvent();
// Create listening socket
ahSocket[0] = ListenFirstFreePort();
rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_ACCEPT );
if( rc == SOCKET_ERROR )
{
printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO );
exit(1);
}
while (TRUE)
{
// Waiting for so;ething to happen
// Basically we'll firstly receive the connexion of the client socket
// and then we'll be notificated when there will be some data to read
// look for events
dwEvent = WSAWaitForMultipleEvents( 2, ahEvents, FALSE, WSA_INFINITE, FALSE);
switch (dwEvent)
{
case WSA_WAIT_FAILED:
printf("WSAEventSelect: %d\n", WSAGetLastError());
break;
case WAIT_IO_COMPLETION:
case WSA_WAIT_TIMEOUT:
break;
default:
//if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket
dwEvent -= WSA_WAIT_EVENT_0;
// enumeration of the events on the socket[dwEvent]
if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents))
{
printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n",
WSAGetLastError(), NetworkEvents.lNetworkEvents);
NetworkEvents.lNetworkEvents = 0;
}
else
{
if (FD_CLOSE & NetworkEvents.lNetworkEvents)
{
printf( "FD_CLOSE ok (dwEvent=%d)\n", dwEvent );
printf( "press a key to exit\n" );
getch(); // require conio.h
WSACloseEvent( ahEvents[0] );
WSACloseEvent( ahEvents[1] );
exit(0);
}
if (FD_READ & NetworkEvents.lNetworkEvents)
{
char szBuffer[256]; int cbRecv;
// Only the second socket expect to receive data
printf( "FD_READ ok (dwEvent=%d)\n", dwEvent );
// read data
cbRecv = recv( ahSocket[dwEvent], szBuffer, sizeof(szBuffer) - 1, 0 );
if( cbRecv <= 0 )
{
printf( "recv() error %d\n", SOCKET_ERRNO );
exit(1);
}
// On ecrit ce paquet (On remet le 0 au cas ou le paquet
// ait ete coupe en 2 - je sais, ca n'arrivera jamais en local)
// we put the 0 in case it has been cut - unlikey to happen on local network
szBuffer[cbRecv] = 0;
// write data in console window
printf( "socket %d : '%s'\n", dwEvent, szBuffer );
}
}
if (FD_ACCEPT & NetworkEvents.lNetworkEvents)
{
struct sockaddr_in addrAccept;
int lenAccept;
lenAccept = sizeof( addrAccept );
// we should have dwEvent=0
printf( "accept ok (dwEvent=%d)\n", dwEvent );
// we create another socket to accept the connexion with the client socket
ahSocket[1] = accept(ahSocket[dwEvent], (struct sockaddr *)&addrAccept, &lenAccept);
// we want to be informed on when we'll be able read data from it
rc = WSAEventSelect(ahSocket[1], ahEvents[1], FD_READ|FD_CLOSE );
if( rc == SOCKET_ERROR )
{
printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO );
exit(1);
}
}
}
}
}
Client
#include <winsock2.h>
#include <conio.h>
#include <time.h>
#pragma comment(lib, "ws2_32.lib")
#define SOCKET_ERRNO WSAGetLastError()
#define ADDRESS "127.0.0.1"
#define PORT 1234
SOCKET ConnectToPort()
{
struct sockaddr_in addr;
SOCKET hSocket;
u_long arg; int err;
// Create socket
hSocket = socket( PF_INET, SOCK_STREAM, 0 );
if( hSocket == INVALID_SOCKET )
{
printf( "socket() error %d\n", SOCKET_ERRNO );
exit(1);
}
// Connexion setting for local connexion
addr.sin_family = AF_INET ;
addr.sin_addr.s_addr = inet_addr(ADDRESS);
addr.sin_port = htons (PORT);
// Connect
if( connect( hSocket, (struct sockaddr *)&addr, sizeof(addr) ) == SOCKET_ERROR )
{
// As we are in non-blocking mode we'll always have the error
// WSAEWOULDBLOCK whichis actually not one
if( SOCKET_ERRNO != WSAEWOULDBLOCK )
{
printf( "connect() error (%d)\n", SOCKET_ERRNO );
exit(1);
}
}
return hSocket;
}
void main()
{
int initClockTime;
WSADATA stack_info;
SOCKET ahSocket[1];
WSAEVENT ahEvents[1];
DWORD dwEvent;
WSANETWORKEVENTS NetworkEvents;
int rc;
// Initialize Winsock
WSAStartup(MAKEWORD(2,0), &stack_info) ;
// Create event
ahEvents[0] = WSACreateEvent();
// Create and connect a socket on the server socket
ahSocket[0]= ConnectToPort();
// not sure if I have to use or not
/*u_long arg = 1;
ioctlsocket( ahSocket[0] , FIONBIO, &arg );*/
// the application wants to receive notification of a completed connection
rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_CONNECT );
if( rc == SOCKET_ERROR )
{
printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO );
exit(1);
}
while (TRUE)
{
// look for events
dwEvent = WSAWaitForMultipleEvents( 1, ahEvents, FALSE, 1000, FALSE);
switch (dwEvent)
{
case WSA_WAIT_FAILED:
printf("WSAEventSelect: %d\n", WSAGetLastError());
break;
case WAIT_IO_COMPLETION:
case WSA_WAIT_TIMEOUT:
break;
default:
printf("while\n");
//if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket
dwEvent -= WSA_WAIT_EVENT_0;
// enumeration of the events on the socket[dwEvent]
if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents))
{
printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents);
NetworkEvents.lNetworkEvents = 0;
}
else
{
if (FD_CONNECT & NetworkEvents.lNetworkEvents)
{
//connexion is OK
printf( "FD_CONNECT ok (dwEvent=%d)\n", dwEvent );
// now that we are connected we want to send data or be aware when the other socket is disconnected
rc = WSAEventSelect(ahSocket[dwEvent], ahEvents[dwEvent], FD_CLOSE | FD_WRITE );
if( rc == SOCKET_ERROR )
{
printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO );
exit(1);
}
}
if (FD_CLOSE & NetworkEvents.lNetworkEvents)
{
printf( "FD_CLOSE ok (dwEvent=%d)\n", dwEvent );
printf( "press a key to exit\n" );
getch();
WSACloseEvent( ahEvents[0] );
exit(0);
}
if (FD_WRITE & NetworkEvents.lNetworkEvents)
{
char szBuffer[256]; int cbBuffer;
printf( "FD_WRITE ok (dwEvent=%d)\n", dwEvent );
// create string and return the size
cbBuffer = sprintf( szBuffer, "Coucou", dwEvent );
// send the string with 0 at the end
rc = send( ahSocket[dwEvent], szBuffer, cbBuffer + 1, 0 );
if (SOCKET_ERROR == rc)
{
printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents);
}
// not sure if I have to use it
//WSAResetEvent(ahEvents[0]);
}
}
}
}
}
download .cpp files : https://www.dropbox.com/s/pjuipz7v4iwr5ea/Clientserver%20TCP.zip
You are not getting FD_WRITE notifications after the first one because you are not taking into account the following paragraph from the documentation:
The FD_WRITE network event is handled slightly differently. An
FD_WRITE network event is recorded when a socket is first connected
with a call to the connect, ConnectEx, WSAConnect, WSAConnectByList,
or WSAConnectByName function or when a socket is accepted with accept,
AcceptEx, or WSAAccept function and then after a send fails with
WSAEWOULDBLOCK and buffer space becomes available. Therefore, an
application can assume that sends are possible starting from the first
FD_WRITE network event setting and lasting until a send returns
WSAEWOULDBLOCK. After such a failure the application will find out
that sends are again possible when an FD_WRITE network event is
recorded and the associated event object is set.
After your first call to send(), the socket is still writable since its outbound buffer is not full. As long as you still have data to send, keep calling send() until it reports an WSAWOULDBLOCK error indicating the buffer is full. At that point, you have to keep track of your remaining data until you get an FD_WRITE notification indicating the socket is writable again so you can continue sending your remaining data from where you left off.
I would recommend to look at regular select() for non-blocking I/O first. Here's couple of links for you to get started:
Non-blocking I/O and select() (the link is broken)
Non-Blocking Sockets in TCP/IP

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.