[Libwebsockets] Send data with libwebsockets

Stefano Sabatini stefasab at gmail.com
Tue Oct 28 17:41:26 CET 2014


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);
            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;
            }
            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;
        }
        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.

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

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

>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.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: send-image-server.c
Type: text/x-csrc
Size: 4156 bytes
Desc: not available
URL: <https://libwebsockets.org/pipermail/libwebsockets/attachments/20141028/17cc7b75/attachment.bin>


More information about the Libwebsockets mailing list