views:

71

answers:

2

My program uses a NetworkOutput object which can be used to write data to a remote server. The semantic is that in case the object is currently connected (because there is a remote server), then the data is actually sent over the socket. Otherwise, it's silently discarded. Some code sketch:

class NetworkOutput
{
public:
  /* Constructs a NetworkOutput object; this constructor should not block, but it
   * should start attempting to the given host/port in the background.
   *
   * In case the connection gets closed for some reason, the object should immediately
   * try reconnecting.
   */
  NetworkOutput( const std::string &hostName, unsigned short port );

  /* Tells whether there is a remote client connected to this NetworkOutput object.
   * Clients can use this function to determine whether they need to both serializing
   * any data at all before calling the write() function below.
   */
  bool isConnected() const;

  /* Write data to the remote client, if any. In case this object is not connected
   * yet, the function should return immediately. Otherwise it should block until
   * all data has been written.
   *
   * This function must be thread-safe.
   */
  void write( const std::vector<char> &data );
};

Right now, I have this implemented using nonblocking sockets. I'n the NetworkOutput constructor, I'm creating a TCP socket as well as an internal helper window. I then do a WSAAsyncSelect call on the socket. This makes the socket nonblocking, and it will cause a magic window message (which I registered myself) to be sent to the internal helper window in case any interesting event (such as 'connection established' or 'connection closed') happens on the socket. Finally, I start a connection attempt using WSAConnect. This returns immediately, and the window procedure of my internal helper window will get notified as soon as the connection succeeded. In case the connection is closed (because the remote client went away), the message procedure will be called and I will attempt to reconnect.

This system allows the me to attach and detach a remote client at will. It works quite well, but unfortunately it requires that I have a message loop running. Without the message loop, the notifications sent by the WSAAsyncSelect call don't seem to arrive at my helper window.

Is there any way to implement a class as described above without requiring a message loop? I was toying around with using blocking sockets in a helper thread, but I couldn't come up with anything reasonable yet. I also considered using a UDP socket, so that I don't even need to connect at all, but I'd like to know whether there is a remote client listening so that in case there is no remote client, the clients of the NetworkOutput class don't need to do any serialization work of complex objects before they can call write().

+3  A: 

You can use WSAEventSelect instead of WSAASyncSelect, which takes the handle of a WSAEVENT instead of a message ID, and then use WSAWaitForMultipleEvents to wait for the event to be signalled.

Instead of WSAEVENT you can also use normal Win32 events created with CreateEvent, and the normal synchronisation functions such as WaitForMultipleObjects.

Phil Devaney
Frerich Raabe
Yes, WSAEVENT is just a typedef for HANDLE, and WSACreateEvent/WSAWaitForMultipleEvents are just thin wrappers around CreateEvent/WaitForMultipleObjects.I don't think send is thread-safe, but I could be wrong. You could look at WSASend, which supports overlapped I/O and might be useful.
Phil Devaney
Accepting this since `WSAEventSelect` seems to work nicely. However, the `WSAEnumNetworkEvents` function is easily among the most cumbersome functions I've used. :-}
Frerich Raabe
A: 

You are looking for the select funtion:

http://support.sas.com/documentation/onlinedoc/sasc/doc750/html/lr2/select.htm

Basically you specify a set of ports you want to listen too.
When called the select deshedules the thread (thus allowing other threads to work while you do a non busy wait). Your thread is woken up after either a time ilimit (usually infinite) a signal (if you want to manually make th thread or the system does) or there is some input that needs to be handeled on any of the ports.

When your thread wakes up it is usually best to let another thread handle the work so; what usually happens is that you create a work object for each port that has data waiting to be read and add these to a queue where a set of worker threads than start handling the input. Once this is done you call select() again to wait for more input.

Note: You don't have to do this it can be done in a single thread.

Martin York
I'm not writing a server, so I'm not waiting for incoming data (which I'd do using `select` on Unix and using `WaitForMultipleObjects` on Windows).
Frerich Raabe