[Libwebsockets] Recovering from partial frame writes

"Andy Green (林安廸)" andy at warmcat.com
Sun May 26 03:28:12 CEST 2013


On 26/05/13 09:12, the mail apparently from Nick Sonneveld included:
> G'day,
>
> I was just wondering what the best way was to recover from partial frame writes.
>
> Looking in test-server.c, the LWS_CALLBACK_HTTP_WRITEABLE callback
> will gracefully handle when the entire file is not sent.  However,
> when sending websockets frames in LWS_CALLBACK_SERVER_WRITEABLE, you
> simply fail if you detect that the entire frame was not written.  I'm
> not sure that manually fragmenting the message will help as I assume
> it's still possible to get partial writes with small frames too?
>
> Would a simple solution be to pass an offset to lws_write() to send
> the remaining packet?  Or perhaps a function to prepare the frame
> first, then send it via several calls to lws_write?

It is possible to do that, however then we need dynamic buffers for each 
connection to persistently hold the remainder of the send packet.  We 
also need a new state but that's less of a problem.  The trend has been 
to try to eliminate malloc in the library so we can work on small 
targets with consistent latencies.

What also puts me off doing it is it hides a basic problem that the OS 
will not reliably accept large sends.  The last time we tried to hide a 
basic problem was that we are singlethreaded, started added apis for 
threaded coding and just confused everyone with unreliable partial 
solutions.

Having said that, it can be done, we could make it a config option and 
small targets can choose to accept the current scheme.

> I guess I don't know enough about the "writability" of a socket. I
> always assumed that if a socket was writable it meant you could write
> more than 1 byte without blocking, but with no guarantees as to the
> maximum number of bytes it would write.  I am happy to be corrected on
> this.

That's right, also there's no way to query what you could send before 
trying because the decision at the OS is highly dynamic.

What we've done that should remove most or all of the sting is for each 
connection, use SO_SNDBUF to reserve the rx buffer size (default 4096 
see handshake.c)

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
		wsi->u.ws.rx_user_buffer = malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			goto bail;
		}
		lwsl_info("Allocating RX buffer %d\n", n);

		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,  &n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			goto bail;
		}

if we don't succeed to get the okay about the tx buffer reservation, we 
don't accept the connection.  So assuming the OS lives up to that 
promise we should not get signalled it's ready for another packet until 
it could handle rx_buffer_size amount, and by then, it can always accept 
a single send of rx_buffer_size.

Is this enough for your situation?

-Andy




More information about the Libwebsockets mailing list