1

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Et7f3XIV
  • 562
  • 1
  • 7
  • 20
  • Polling using e.g. `select`? Non-blocking sockets? – Some programmer dude Jul 19 '17 at 14:48
  • 3
    There are multiple ways but one is to use the overlapped I/O mode of `WSARecv` and use `MsgWaitForMultipleObjects` to wait for either a window message or activity on the socket handle. This is sort-of half wrapped in the Winsock API but in modern Windows implementations sockets are actually just HANDLEs under the hood. Admittedly there are more straightforward options but the advantage is that it can be smoothly extended with additional handle types for I/O multiplexing. – doynax Jul 19 '17 at 15:13
  • Have you portable way? – Et7f3XIV Jul 19 '17 at 15:15
  • @Et7f3XIV: Portable to what? I uncertain whether the move to kernel HANDLEs was is made in transition from Win16 or if the Win9x family also lacks support. I hope you are not forced to maintain support for such museum pieces but I believe that `WSAAsyncSelect` with window message notifications was the traditional Winsock 1.1 (Win16) method if you need to extend your portability that far. – doynax Jul 19 '17 at 15:30
  • I want port my program on linux. (I know getMessage isn't portabe but i prefer use only portable function. i tghink i'll make thread only for the getmessage loop. – Et7f3XIV Jul 19 '17 at 15:51
  • 1
    @Et7f3XIV: Not really. There is plain old `select` as previously mentioned if you only need multiplexing among sockets but then you can't wait for incoming inter-communication, say for outgoing data or cancellation. Polling with intermittent sleeps is about the best you could do. Really though, achieving this type of portability without libraries is in my experience a lost cause. You will be better off in the long run isolation the I/O into system-specific wrappers while pulling out the generic bits as into shared modules. – doynax Jul 19 '17 at 16:02
  • 1
    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. – Martin James Jul 19 '17 at 16:28
  • I've read some other article and I think the better way to go is like @Martin James say. I will refactor my code for including thread.@doynax I would apply if my programm is windows only. – Et7f3XIV Jul 19 '17 at 16:38
  • @MartinJames put you're comment in answer for accepting your answer. – Et7f3XIV Jul 19 '17 at 16:39
  • @Martin James: I tend to agree for applications allowing straightforward locking. Unfortunately I find that I usually still require multiplexing even on the I/O thread for transmits outside of pure stop-and-wait applications, time-outs, and cancellation. – doynax Jul 19 '17 at 16:39
  • @doynax sure. In requirements like this, there is no need for any expicit locks - it's all message-passing, (obviously, the Windows message queues etc. have their own internal locks, but you can at least be sure that they work:). Timeouts/cancellation issues may exist, maybe the network thread might need to loop around and reconnect after a server or network fail, all sorts of stuff. That's even more of a reason to implement all of the network stuff in a simple synchronous manner in a thread of its own - no matter what gets blocked/delayed/whatever, the GetMessage loop runs on, isolated:) – Martin James Jul 19 '17 at 18:00
  • @Martin James: I was referring to user interface actions requiring immediate action without waiting for pending receive to complete. Think application shutdown, or an outgoing packet. As for locking there is frequently additional application state involved in processing network traffic and requiring careful sharing. Personally I tend to make the thread the manager of the connection and its state, permitting an imperative style and updated via messaging though requiring UI caching. If only message parsing/encoding is performed there is unlikely to be sufficient CPU load to warrant threading. – doynax Jul 19 '17 at 18:13

2 Answers2

4

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 ... 
        }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • with this example all my code will be in this loop... (thx for editing my original post) – Et7f3XIV Jul 19 '17 at 21:14
  • @Et7f3XIV it would be better if the window has a wndproc function and you handle the messages in there instead of in the loop itself. But either will work. – Remy Lebeau Jul 20 '17 at 00:37
1

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

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • *"it's much easier to test, debug"* - Not at all. Concurrent code is inherently harder to test and debug. – IInspectable Jul 19 '17 at 17:56
  • @IInspectable not when you have isolated subsystems with message-passing. Each subsystem does not know, or need to know, what the others are doing, just what they are saying:) – Martin James Jul 19 '17 at 18:05
  • I choose to avoid thread becaus I don't wants to go in thread's mess like concurrent access, race condition,... but you have well argumented so I will use pthread and notify the messageloop with postmessage. thx – Et7f3XIV Jul 19 '17 at 21:00