Products > Networking & Wireless

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

<< < (3/4) > >>

Nominal Animal:

--- Quote from: peter-h on December 22, 2022, 09:16:31 pm ---I can't find errnoto.
--- End quote ---
I, uh, missed a space in there.  It's 'errno'. :-[

The return value of write() is either positive, or -1 to indicate an error.  (It should never return zero.)  When it returns -1, examine errno to see what caused it.  My bet is that errno == ERR_VAL, per my previous message.

This is completely untested, but here's how I'd write the code, when you have rx_len > 0 in buf:

--- Code: ---    // Also omitted: check that ethser_port[i].client_fd != -1, before rx_len = KDE_serial_receive(i, buf, rx_len);

    size_t  tx_len = 0;

    while (tx_len < rx_len) {
        ssize_t written;

        errno = 0;
        written = write(ethser_port[i].client_fd, buf + tx_len, rx_len - tx_len);
        if (written > 0) {
            tx_len += n;
        } else
        if (errno == ERR_VAL) {
            // yield()? sleep()? For one half of TCP send timeout interval?
        } else {
            // Something went wrong.  'errno' should contain the error code.
            shutdown(ethser_port[i].client_fd, SHUT_RDWR);
            ethser_port[i].client_fd = -1;

--- End code ---

The return value of write() I am seeing is always one of

1 - if I am sending keystrokes
512 - if I am sending big blocks (and then the last block will be < 512 although I have not actually checked this)
-1 - if there is an error, and this corresponds to "too much data too fast"

Your code if of course much neater than mine :)

I didn't realise that one could do

buf + tx_len

being same as


I knew buf is same as &buf[0]. Learn something every day :)

I am now testing with various size blocks and with various bottlenecks at the far end (various output UART baud rates). I am getting interesting results. With 1200 baud output, and USB VCP input of 25kbytes/sec, and that's a real proper output bottleneck, I can send ~7k without data loss.

7k is in the right ballpark for the various buffers in the system. 1k on each UART, 512 bytes to service the sockets, a few k in LWIP. Yet this means that flow control must be working over the LAN. So this is more complicated. But I am happy to document this, as a system intended for half duplex applications (fairly essential anyway since a server cannot initiate a connection, so the master will be at the client end), and packets no bigger than a few k.

Nominal Animal:
Do the devices connected to the UARTs have hardware flow control, RTS/CTS?  This is necessary for not dropping any data in your scenario.

The trick of managing CTS over a long pipe is to initially pass the buffer size, and then report whenever additional bytes have been written.

For example, if we consider data flow A→B, and A knows B has 512 byte buffer, then:
    A: Here is 500 bytes.
     (A knows B has room for 12 additional bytes.)
    B: Received 500 bytes.
    B: Wrote 100.
     (A knows B has room for 112 additional bytes.)
    A: Send 52 bytes.
     (A knows B has room for 60 additional bytes.)
    B: Received 52 bytes.
    B: Wrote 200.
     (A knows B has room for 260 additional bytes.)
This way A knows exactly when B can receive more data.  If the device connected to A supports hardware flow control, then A keeps CTS asserted whenever it has room in its UART receive buffer.

In a real-world implementation, the problem is that we need a separate metadata channel to report how many bytes have been written.  One could use TCP urgent data (say 16-bit network-endian wrote count) –– the "received N bytes" being inherent in the protocol ––, but LWIP does not support urgent data currently.

Yes; that aspect I am aware of (serial comms has been my day job for 40 years).

However, what I did was a software hack at the RH end, whereby the data is just dumped, so the output UART baud rate is not limiting the flow. In that situation, no data should be lost, because flow control should work along the preceeding sections.

I am finding that write() returns -1 sometimes. This is very rare and happens after ~50 512-byte packets. Predictably, some data is then lost.

There was that code which, upon getting -1, closed the socket and attempted to clean up a bit, and new incoming data would re-open the connection, but that didn't work, so I just took it out.

Curiously, I am never seeing short writes to the socket.

On the other end I see no error condition ever

--- Code: ---    //read data from TCP connection for transmit
          int tx_len = serial_get_opqspace(i);
          if (tx_len > sizeof(buf)) tx_len = sizeof(buf);

          //tx_len contains the maximum number of characters the UART buffer can hold. IOW
          //tx_len has the maximum number of characters we can read from the socket.
          if (tx_len > 0)
            tx_len = read(ethser_port[i].client_fd, buf, tx_len);

            //Now tx_len is either >0 when data has arrived, 0 when the remote side
            //closed the socket or -1 to indicate no data or an error.
            if (tx_len > 0)
              //we have data, send it to the UART
              serial_transmit(i, buf, tx_len);
              ethser_port[i].tx_count += tx_len;

            //Check socket closed or an error occured (except for when the socket
            //return no data in which case errno is set to EWOULDBLOCK).
            if ((tx_len <= 0) && (errno != EWOULDBLOCK))
              //Something went wrong or remote closed. Close the client socket

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

              ethser_port[i].client_fd = -1;
              continue;  //break from the for-loop early; no need to continue with this socket

--- End code ---

Just had an idea. Maybe some buffer size is marginal. I will have a play with lwipopts.h. EDIT: tried a load, makes no real difference.

I think, enough time spent on this. I am going to document this as a half duplex system, max packet size 1k unless baud rates chosen to throttle data appropriately. In reality most applications are half duplex; this is not a 1980s-style terminal server :)

Quite weird though. Even more weird that the short writes are never seen. The full 512 bytes are always written, or -1.

Nominal Animal:

--- Quote from: peter-h on December 23, 2022, 09:33:38 am ---Yes; that aspect I am aware of (serial comms has been my day job for 40 years).
--- End quote ---
Don't be offended; I just cannot help but mention things that could be useful for others as well.  There have been several threads about serial port forwarding, so I just wanted everyone else possibly reading this thread to understand the possible issues for buffer overruns here.  I always write this way, and it absolutely should not be taken as if I believed you didn't know: I write these things so that everyone else is aware of these things as well.

--- Quote from: peter-h on December 23, 2022, 09:33:38 am ---However, what I did was a software hack at the RH end, whereby the data is just dumped, so the output UART baud rate is not limiting the flow. In that situation, no data should be lost, because flow control should work along the preceeding sections.
--- End quote ---
The multiple sequential buffers and the latencies inherent in flushing a buffer does cause a very interesting phenomena, though.
Even when you have more than enough bandwidth, you can still get congestion due to the latencies, if you have multiple sequential buffers.

There is a good traffic analogy: consider an occasional slightly longer latency, that causes the traffic to stop, just like they stop at street lights.
In theory, the entire queue could move as one when the light switches to green, but that's not usually what happens: the cars accelerate individually, with the next one starting to accelerate only when the one in front has pulled a certain distance away.

This buffering-latency-inchworm-effect (I bet network people have a better name for this!) is excarberated by the number of buffers you have in sequence.
You can have data flowing very well for quite some time, and then suddenly stuff just backs up, and takes a relatively long time to regain the previous throughput.

Like frogs jumping in a queue: if only they jumped in sync, they'd make much faster progress.  Their overall speed (data throughput) ends up being determined by their jump phases (buffer fill-flush latencies)!

--- Quote from: peter-h on December 23, 2022, 09:33:38 am ---Curiously, I am never seeing short writes to the socket.
--- End quote ---
If all downstream buffers are a multiple of 512 bytes in size, and the TCP MTU is large enough to never split 512 byte packets, then this is to be expected: either the buffer is full, or it has room for a multiple of 512 bytes, all down the buffer chain.


[0] Message Index

[#] Next page

[*] Previous page

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