Products > Networking & Wireless

Can one find out how much data can be sent to a NON blocking socket without loss

(1/4) > >>

peter-h:
This is LWIP.

I have code which reads a serial port buffer, finds out how many bytes are in it, and transmits that to a socket.

Unfortunately the socket is non-blocking so if the data is arriving too fast, some gets lost.

The obvious solution (make the socket blocking) would cause problems elsewhere.

Is there some way to get how many bytes a write socket can accept?

For reading from a socket, there appears to be a hack where you give it a zero block length, but I can't find anything for writing to a socket.


--- Code: ---          if ((rx_len > 0) )
          {
            //read rx_len bytes from the buffer and send over ethernet
            rx_len = serial_receive(i, buf, rx_len);
            if (rx_len > 0)
            {
              if (write(ethser_port[i].client_fd, buf, rx_len) <0)
              {
                //something went wrong. Close the client socket

                dbg_printf("EthSer Closing client idx: %d fd: %d", i, ethser_port[i].client_fd);

                close(ethser_port[i].client_fd);
                ethser_port[i].client_fd = -1;
                continue;  //break from the for-loop early
              }

              ethser_port[i].rx_count += rx_len;

            }
          }
--- End code ---

where write() is



Googling suggests there may be a way using something like this


--- Code: ---//socket ready for writing
        if(FD_ISSET(new_sd, &write_flags)) {
            //printf("\nSocket ready for write");
            FD_CLR(new_sd, &write_flags);
            send(new_sd, out, 255, 0);
            memset(&out, 0, 255);
        }   //end if
--- End code ---

but that isn't going to tell me the socket's available tx buffer space.

Nominal Animal:
Who told you lwip_write() will never return a short count?  They lied.  :'(

The return value will tell you how much of the data was buffered (if there was no error).  You can discard that amount, but not all you tried to send.

peter-h:
That function ends up in


--- Code: ---int
lwip_write(int s, const void *data, size_t size)
{
  return lwip_send(s, data, size, 0);
}

--- End code ---

Surprisingly (this function is all over the place) I can't find a definition what this returns but from what you say it sounds like it returns #bytes actually written and the rest it discarded. Is that right?

I may be able to work with that, with a bit of a hack, because the "discarded" data has already been read out of the UART rx buffer so I can't just dump it; I have to call lwip_write again and repeat until it has all gone.

But a much better way would be some kind of a queue_space() function for lwip_send(). I would call it before reading the UART rx buffer and read out of that buffer the lesser figure.

This code is not mine and I am trying to make it work with correct flow control. TCP does flow control already but in this case the code is loading data into a nonblocking socket without first checking how much space there is.

OTOH, a breakpoint in the code checking for a return value < 0 is taken so lwip_send is returning a negative value.

Nominal Animal:
lwip_send() is implemented in src/api/sockets.c:lwip_send().
If this is a TCP socket, then netconn_write_partly() will be called.
If this is a non-TCP socket, then lwip_sendto() (defined later in the same file) will handle the write, and if it cannot be sent as a single packet, an ERR_MEM error will be returned.

(This means that if you use a non-TCP socket, and your write() returns ERR_MEM, you should try halving the buffer size until it succeeds or you get a buffer size that is not worth halving, that is better sent in a single packet.  Say 64 bytes or so.)

src/api/api_lib.c:netconn_write_partly() calls netconn_write_vectors_partly() (defined later in the same file), and will modify the number of bytes written, reflecting the number of bytes consumed from the start of the buffer, and either an error, or this amount, will eventually be returned by the original call.


--- Quote from: peter-h on December 21, 2022, 10:54:21 pm ---But a much better way would be some kind of a queue_space() function for lwip_send(). I would call it before reading the UART rx buffer and read out of that buffer the lesser figure.

--- End quote ---
Unfortunately, the internal structure of LWIP is such that the amount of free space available in the transmit buffer is not visible at this level.  The netconn_write_vectors_partly() only finds out how much data was successfully sent by examining the structure modified by the src/api/api_lib.c:netconn_apimsg() call, which basically calls the "lower part" of the API function.  It is this lower part that determines how much data can be buffered (for a TCP socket here in this case; the non-TCP socket case allocates a temporary buffer dynamically).


I don't see many sensible options here, besides the obvious "use a dedicated UART rx buffer, and only read out as much as there is free room in that buffer".

ejeffrey:

--- Quote from: peter-h on December 21, 2022, 10:54:21 pm ---Surprisingly (this function is all over the place) I can't find a definition what this returns but from what you say it sounds like it returns #bytes actually written and the rest it discarded. Is that right?

--- End quote ---

It's not "discarded".  It's still in your buffer, it just wasn't transfered to the network buffers.  You might be discarding it by ignoring the return count and deallocating or reusing the buffer but that is you not the send function.


--- Quote ---I may be able to work with that, with a bit of a hack, because the "discarded" data has already been read out of the UART rx buffer so I can't just dump it; I have to call lwip_write again and repeat until it has all gone.

But a much better way would be some kind of a queue_space() function for lwip_send(). I would call it before reading the UART rx buffer and read out of that buffer the lesser figure.

--- End quote ---

No, your way is much worse.  It is inherently not reentrant or thread safe.  It depends on the precise internal architecture (and I don't have a ton of experience with lwip, so this will be more general) but it is usually not possible to guarantee that if your proposed queue_space function returns N, that a subsequent call to send will actually be able to write N bytes.  What if a network interrupt happens and fills up the buffers with received data before you get around to actually sending the data?

You would have to check the return code either way, so checking the buffer capacity before sending is at best an optimization and likely prone to misuse.

The only way to make your proposed API work is to have the queue_space actually reserve buffers for a future send call.  Then an intervening receive interrupt will just have to discard it's data since the buffers are already allocated.  But then if you never end up sending the data, that buffer just sits there reserved doing nothing.

The way you need to handle this is that you read a block of data from the serial port, the try resending it repeatedly, checking the return value each time and incrementing the start offset.  That's basically what the blocking call does, but the non blocking call gives you the opportunity to give up at any point.

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod