[Libwebsockets] Send data with libwebsockets

Andy Green andy at warmcat.com
Wed Oct 29 00:53:11 CET 2014



On 29 October 2014 00:41:26 GMT+08:00, Stefano Sabatini <stefasab at gmail.com> wrote:
>In data Tuesday 2014-10-28 07:27:15 +0800, Andy Green ha scritto:
>
>Thanks for the quick reply.
> 
>> On 28 October 2014 05:08:52 GMT+08:00, Stefano Sabatini
><stefasab at gmail.com> wrote:
>> >Hi,
>> >
>> >I'm a newbie with libwebsockets and I think I'm doing something
>wrong
>> >with it.
>> 
>> Yeah there are a couple of problems with your code.
>> 
>> >I have this code in my protocol callback:
>> > 
>> >    case LWS_CALLBACK_RECEIVE:
>> >        printf("Received data: %s\n", (char *)in);
>> >        /* send JPEG compressed image */
>> >        {
>> >#define PAYLOAD_SIZE 4096
>> >char buf[LWS_SEND_BUFFER_PRE_PADDING + PAYLOAD_SIZE +
>> >LWS_SEND_BUFFER_POST_PADDING];
>> 
>> This is good.
>> 
>> >            int n;
>> >            // read data from file
>> >            const char *filename = "lena.jpeg";
>> >            FILE *f = fopen(filename, "r");
>> >            if (!f) {
>> >               fprintf(stderr, "Could not open file '%s'\n",
>filename);
>> >                break;
>> >            }
>> >
>> >            printf("Sending image data...\n");
>> >while (n = fread(buf + LWS_SEND_BUFFER_PRE_PADDING, 1, PAYLOAD_SIZE,
>> >f)) {
>> 
>> You can't loop like this.
>> 
>> lws should never do anything that will block.
>> 
>> If lena.jpg is big enough and depending on network conditions to the
>> peer, this will always block.  Other connections or input on this
>> connection will not get processed while it blocks.
>> 
>> >                printf("Sending %d bytes...\n", n);
>> >       // memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 255,
>PAYLOAD_SIZE);
>> >libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], n,
>> >LWS_WRITE_TEXT);
>> 
>> Eventually writing more on the socket won't be accepted by the
>kernel.
>> 
>
>> What you should be doing is only writing on the connection in the
>> LWS_CALLBACK_SERVER_WRITEABLE callback, and if you have something
>> you want to write, arrange to get a WRITEABLE callback using
>> libwebsocket_callback_on_writable(context, wsi); and send one
>> fragment of your message each time.
>
>Now I have this in my code:
>
>static int send_image_protocol_callback(struct libwebsocket_context
>*context,
>                                        struct libwebsocket *wsi,
>                             enum libwebsocket_callback_reasons reason,
>                        void *priv_user_data, void *in, size_t in_size)
>{
>#define PAYLOAD_SIZE 4096
>char buf[LWS_SEND_BUFFER_PRE_PADDING + PAYLOAD_SIZE +
>LWS_SEND_BUFFER_POST_PADDING];
>    struct send_image_protocol_user_data *user_data = priv_user_data;
>
>    switch (reason) {
>    case LWS_CALLBACK_ESTABLISHED:
>        printf("New Connection established with client\n");
>        break;
>
>    case LWS_CALLBACK_SERVER_WRITEABLE:
>    {
>        /* send data segment */
>        int n, flags = LWS_WRITE_BINARY;
>
>        if (user_data->is_end) {
>            printf("Terminating...\n");
>  libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 0, flags);

He won't send a zero-length packet I think.

>            fclose(user_data->file);
>            user_data->file = NULL;
>        } else {
>n = fread(buf + LWS_SEND_BUFFER_PRE_PADDING, 1, PAYLOAD_SIZE,
>user_data->file);
>            if (n == 0) {
>                user_data->is_end = 1;
>                break;
>            }

Nope.  Use something like

        if (!feof(user_data->file))
                flags |= LWS_WRITE_NO_FIN;

and get rid of the whole is_end thing.

>            printf("Sending %d bytes...\n", n);
>            flags |= LWS_WRITE_NO_FIN;
>  libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], n, flags);
>            libwebsocket_callback_on_writable(context, wsi);
>        }
>    }
>    break;
>
>    case LWS_CALLBACK_RECEIVE:
>        printf("Received data: %s\n", (char *)in);
>        /* now we want to start sending data */
>        const char *filename = "lena.jpeg";
>        user_data->file = fopen(filename, "r");
>        if (!user_data->file) {
>            fprintf(stderr, "Could not open file '%s'\n", filename);
>            break;
>        }

Check feof(user_data->file) here and bail out if so.

>        user_data->is_end = 0;
>        libwebsocket_callback_on_writable(context, wsi);
>        printf("Sending image data from file %s\n", filename);
>        break;
>
>    case LWS_CALLBACK_CLOSED:
>        printf("Connection closed\n");
>        break;
>    }
>
>    return 0;
>}
>
>...
>
>If I understand correctly I have to call
>libwebsocket_callback_on_writable() when I want the protocol callback
>to handle the LWS_CALLBACK_SERVER_WRITEABLE again (if I try to disable
>those calls indeed that callback is not handled anymore, and the
>server freezes). So I'm calling libwebsocket_callback_on_writable() in
>these two occasions:
>1. when I receive a message from the client, meaning that I have to
>send the data
>
>2. when I send a fragment, and there is still another fragment to send.

Sounds good.

>> This way even extremely laggy or low bandwidth connections will
>> regulate themselves without blocking and you can handle many (tens
>> of thousands) of simultaneous connections cleanly.
>
>Makes sense. 
>
>> And this is sending one logical websocket message per 4K or
>> whatever.  It's probably not what you want.  Have a look in
>> test-fraggle.c to see how to use LWS_WRITE_NO_FIN.
>
>My understanding is that I have to set NO_FIN on the last packet. But
>then I get this:

You want a FIN *only* on the last fragment, it's the opposite of your understanding.  Your code above has the right idea but it does not handle the last read from the file correctly.

FIN bit indicates this fragment completes a ws message.  So you want to withold FIN (NO_FIN) on all the fragments except the last one in your message.

>New Connection established with client
>Received data: Hello WebSocket!
>Sending image data from file lena.jpeg
>Sending 4096 bytes...
>Sending 4096 bytes...
>Sending 4096 bytes...
>Sending 4096 bytes...
>Sending 17 bytes...
>lwsts[21381]: error on reading from skt
>
>This is not still working (usually it bails out after a few packets),
>or before sending the final 0-sized packet.
>
>Indeed I'm not sure about how should I use NO_FIN and CONTINUATION (I
>tried with several combinations of them, none worked so far), and I
>can see no documentation about them in libwebsockets.h

test-fraggle shows how to use them.

-Andy

>From firefox debugging I see:
>
>The connection to ws://127.0.0.1:9999/ was interrupted while the page
>was loading.
>
>> 
>> >            }
>> >            fclose(f);
>> >        }
>> >        break;
>> >
>> >
>> >This causes a failure on the client side, which closes the
>connection.
>> 
>> I can't say why but probably because it's getting several ws messages
>each with a piece of jpeg in them.
>> 
>> >Question is: if we have to send data using several segments, what's
>> >the supposed way to do it? Or should I assemble the complete data
>and
>> >send it with a single call to libwebsocket_write()?
>> 
>> No see above about fraggle.
>> 
>> >Also sometimes I'm getting this on the server side:
>> >Sending image data...
>> >Sending 4096 bytes...
>> >Sending 4096 bytes...
>> >Sending 4096 bytes...
>> >lwsts[13497]: ****** 7e53e0 Sending new, pending truncated ...
>> >hellowebsockets: /home/stefano/src/libwebsockets/lib/output.c:112:
>> >lws_issue_raw: Assertion `0' failed.
>> >
>> >Is this a bug, should I fill a bug report for that?
>> 
>> Please use current lws from git.
>
>I'm using a recent git. I'm attaching the complete test code, I'd
>appreciate your comments.
>
>
>------------------------------------------------------------------------
>
>_______________________________________________
>Libwebsockets mailing list
>Libwebsockets at ml.libwebsockets.org
>http://ml.libwebsockets.org/mailman/listinfo/libwebsockets




More information about the Libwebsockets mailing list