How to combine RECV and getMessage in loop (C, WINAPI)

627 Views Asked by At

How do you code this in C?

The desired flow is:

Create socket
Create window
loop: Wait until data can be read from socket or message are added to the queue
if data then
  do stuff with data
  goto loop
else if message then
  do stuff with message
  goto loop

I have tried this code:

MSG msg;
DWORD last=msg.time;
short bRet;
char command[20];
int n=0;
while((
       (n = recv(sock, command, sizeof command - 1, 0)) >= 0
       )||(
           (bRet = GetMessage( &msg, 0, 0, 0 )) != 0
           //I can't use peek message because it will have the same issue than non-blocking socket
           )){
    //here we check time of message if it is equal to last we know that is socket
}

I know threads exist, but I want to avoid use of threads. I will use a thread if it is the only way to go.

Edit: Using a non-blocking socket is not a solution because if no data is available and no message is in the queue then my program will exit.

2

There are 2 best solutions below

3
On BEST ANSWER

I would have surely gone with a separate thread for the network recv() and all associated protocol checking/parsing, so isolating it from the message-handling loop. Such a thread would be reasonably portable. The thread would generate 'command' struct instances, (malloc), and fire them into an extern handler function. On Windows, that handler would load the struct address into lParam/hParam of a WM_APP message and post it to the GetMessage() thread, where it would be received in the usual way, dispatched to a handler, the struct extracted, executed and then freed.

Such a design might be seen as over-complex, (and uses the dreaded threads), but it's much easier to test, debug, extend and enhance than cramming everything into an asynchronous message loop that does everything. Sometimes, an extra thread really is easier:)

2
On

Use the socket in asynchronous (not non-blocking) mode via WSAAsyncSelect():

The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

The socket will notify a specified HWND of socket activity, like data available.

The app can then run a standard message loop, processing socket messages when they arrive from the queue.

#define WM_SOCKET_MSG (WM_APP + 1)

WSAAsyncSelect(sock, hwnd, WM_SOCKET_MSG, FD_READ | FD_WRITE | FD_CLOSE);

MSG msg;
char command[20];
int n;

while (GetMessage(&msg, 0, 0, 0)) {
        switch (msg.message) {
            case WM_SOCKET_MSG:
            {
                int event = WSAGETSELECTEVENT(msg.lParam);
                int error = WSAGETSELECTERROR(msg.lParam);
                if (error != 0) {
                    // process socket error as needed ...
                    break;
                }

                switch(event) {
                    case FD_READ:
                        n = recv(sock, command, sizeof command - 1, 0);
                        if (n > 0) {
                            // process data as needed ...
                        } else {
                            // process read error as needed ... 
                        }
                        break;
                    }

                    // process other socket events as needed ...
                }

                break;
            }

            // process other messages as needed ... 
        }
}