warmcat / libwebsockets

canonical libwebsockets.org networking library
https://libwebsockets.org
Other
4.73k stars 1.48k forks source link

I can't receive LWS_CALLBACK_CLIENT_RECEIVE reason event #1612

Closed fusionJose closed 5 years ago

fusionJose commented 5 years ago

Hi,

I have made a simple test lws client project to connect a websocket client with a server and exchange some data. Connecting to the websocket server works, sending a message to that server works too. When it comes to receiving message from websocket server, i cannot receive LWS_CALLBACK_CLIENT_RECEIVE event. I could verify that the server is sending back the response to my client. The messages are in JSON format and here are both messages, discovered by other aplication:

[2,"192339","BootNotification",{"chargePointVendor":"MilHouse","chargePointModel":"Fusion"}] [3,"192339",{"status":"Accepted","currentTime":"2019-06-20T13:50:47.651Z","interval":14400}]

And here is my code:

`#include

include

include

include

include

include

static int bExit; static int bDenyDeflate = 1;

static int callback_test(struct lws wsi, enum lws_callback_reasons reason, void user, void* in, size_t len);

static int zero_length_ping = 0;

// Escape the loop when a SIGINT signal is received static void onSigInt(int sig) { bExit = 1; }

// The registered protocols static struct lws_protocols protocols[] = { { "ocpp1.6", // Protocol name callback_test, // Protocol callback 0, // Data size per session (can be left empty) 512, // Receive buffer size (can be left empty)

},
{ NULL, NULL, 0 } // Always needed at the end

};

// The extensions LWS supports, without them some requests may not be able to work static const struct lws_extension extensions[] = { { "permessage-deflate", lws_extension_callback_pm_deflate, "permessage-deflate; client_max_window_bits" }, { "deflate-frame", lws_extension_callback_pm_deflate, "deflate_frame" }, { NULL, NULL, NULL } // Always needed at the end };

// List to identify the indices of the protocols by name enum protocolList { PROTOCOL_TEST,

PROTOCOL_LIST_COUNT // Needed

};

// Callback for the test protocol static int callback_test(struct lws wsi, enum lws_callback_reasons reason, void user, void* in, size_t len) { // The message we send back to the echo server const char msg[128] = "Simple webserver echo test!";

// The buffer holding the data to send
// NOTICE: data which is sent always needs to have a certain amount of memory (LWS_PRE) preserved for headers
unsigned char buf[LWS_PRE + 128];

// Allocating the memory for the buffer, and copying the message to it
memset(&buf[LWS_PRE], 0, 128);
strncpy((char*)buf + LWS_PRE, msg, 128);

int n;

json_object *jarray = json_object_new_array();
json_object *jobj_type = json_object_new_object();
json_object *jobj_ID = json_object_new_object();
json_object *jobj_action = json_object_new_object();
json_object *jobj = json_object_new_object();

jobj_type = json_object_new_int(2); /* A call from client to server */

jobj_ID = json_object_new_string("1946577");
jobj_action = json_object_new_string("BootNotification");

char *str = "{\
        \"chargePointVendor\": \"MilHouse\", \
        \"chargePointModel\": \"Fusion\", \
    }";

json_object_array_add(jarray,jobj_type);
json_object_array_add(jarray,jobj_ID);
json_object_array_add(jarray,jobj_action);

jobj = json_tokener_parse(str);

json_object_array_add(jarray,jobj);

// For which reason was this callback called?
switch (reason)
{
    // The connection closed
case LWS_CALLBACK_CLOSED:
    printf("[Test Protocol] Connection closed.\n");
    break;

    // Our client received something
case LWS_CALLBACK_CLIENT_RECEIVE:
    printf("[Test Protocol] Received data: \"%s\"\n", (char*)in);

    bExit = 1;

    break;

    // Here the server tries to confirm if a certain extension is supported by the server
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
    if (strcmp((char*)in, "deflate-stream") == 0)
    {
        if (bDenyDeflate)
        {
            printf("[Test Protocol] Denied deflate-stream extension\n");
            return 1;
        }
    }
    break;

    // The connection was successfully established
case LWS_CALLBACK_CLIENT_ESTABLISHED:
    printf("[Test Protocol] Connection to server established.\n");

    lws_callback_on_writable(wsi);

    break;

    // The server notifies us that we can write data
case LWS_CALLBACK_CLIENT_WRITEABLE:
    printf("[Test Protocol] The client is able to write.\n");

    printf("[Test Protocol] Writing \"%s\" to server.\n", msg);

    printf("[Test Protocol] The client is able to write.\n");
    char ping[LWS_PRE + 250];
    int m;

    printf("jobj from str:\n---\n%s\n---\n", json_object_to_json_string_ext(jarray, JSON_C_TO_STRING_PLAIN));

    n = 0;

    n = lws_snprintf((char *)ping + LWS_PRE, 250,
        json_object_to_json_string_ext(jarray, JSON_C_TO_STRING_PLAIN));

    lwsl_user("Sending PING %d...\n", n);

    m = lws_write(wsi, ping + LWS_PRE, n, LWS_WRITE_TEXT);
    if (m < n) {
        lwsl_err("sending ping failed: %d\n", m);
    }

    break;

    // There was an error connecting to the server
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
    printf("[Test Protocol] There was a connection error: %s\n", in ? (char*)in : "(no error information)");
    break;

default:
    break;
}

return 0;

}

// Main application entry int main() { lws_set_log_level(LLL_ERR | LLL_WARN, lwsl_emit_syslog); // We don't need to see the notice messages

signal(SIGINT, onSigInt); // Register the SIGINT handler

// Connection info
char inputURL[300] = "ws://192.168.12.110:8080/steve/websocket/CentralSystemService/Test1234";
int inputPort = 80;

struct lws_context_creation_info ctxCreationInfo; // Context creation info
struct lws_client_connect_info clientConnectInfo; // Client creation info
struct lws_context *ctx; // The context to use

struct lws *wsiTest; // WebSocket interface
const char *urlProtocol, *urlTempPath; // the protocol of the URL, and a temporary pointer to the path
char urlPath[300]; // The final path string

// Set both information to empty and allocate it's memory
memset(&ctxCreationInfo, 0, sizeof(ctxCreationInfo));
memset(&clientConnectInfo, 0, sizeof(clientConnectInfo));

clientConnectInfo.port = inputPort; // Set the client info's port to the input port

if (lws_parse_uri(inputURL, &urlProtocol, &clientConnectInfo.address, &clientConnectInfo.port, &urlTempPath))
{
    printf("Couldn't parse URL\n");
}

// Fix up the urlPath by adding a / at the beginning, copy the temp path, and add a \0 at the end
urlPath[0] = '/';
strncpy(urlPath + 1, urlTempPath, sizeof(urlPath) - 2);
urlPath[sizeof(urlPath) - 1] = '\0';

clientConnectInfo.path = urlPath; // Set the info's path to the fixed up url path

// Set up the context creation info
ctxCreationInfo.port = CONTEXT_PORT_NO_LISTEN; // We don't want this client to listen
ctxCreationInfo.protocols = protocols; // Use our protocol list
ctxCreationInfo.gid = -1; // Set the gid and uid to -1, isn't used much
ctxCreationInfo.uid = -1;
ctxCreationInfo.extensions = extensions; // Use our extensions list

// Create the context with the info
ctx = lws_create_context(&ctxCreationInfo);
if (ctx == NULL)
{
    printf("Error creating context\n");
    return 1;
}

// Set up the client creation info
clientConnectInfo.context = ctx; // Use our created context
clientConnectInfo.ssl_connection = 0; // Don't use SSL for this test
clientConnectInfo.host = clientConnectInfo.address; // Set the connections host to the address
clientConnectInfo.origin = clientConnectInfo.address; // Set the conntections origin to the address
clientConnectInfo.ietf_version_or_minus_one = -1; // IETF version is -1 (the latest one)
clientConnectInfo.protocol = protocols[PROTOCOL_TEST].name; // We use our test protocol
clientConnectInfo.pwsi = &wsiTest; // The created client should be fed inside the wsi_test variable

printf("Connecting to %s://%s:%d%s \n\n", urlProtocol, clientConnectInfo.address, clientConnectInfo.port, urlPath);

// Connect with the client info
lws_client_connect_via_info(&clientConnectInfo);
if (wsiTest == NULL)
{
    printf("Error creating the client\n");
    return 1;
}

// Main loop runs till bExit is true, which forces an exit of this loop
while (!bExit)
{
    // LWS' function to run the message loop, which polls in this example every 50 milliseconds on our created context
    lws_service(ctx, 50);
}

// Destroy the context
lws_context_destroy(ctx);

printf("\nDone executing.\n");

return 0;

}`

lws-team commented 5 years ago

Disabling permessage-deflate / setting ctxCreationInfo.extensions = NULL fixes it?

fusionJose commented 5 years ago

No, it continues without receiving the response.

I get the code, and adapted to my aplication from here: https://gist.github.com/iUltimateLP/17604e35f0d7a859c7a263075581f99a

lws-team commented 5 years ago

adapted to my aplication from here

I'm not responsible for someone else's code... I'm not really responsible for my own under a FOSS license but at least I share an interest in keeping it working all things equal.

What I suggest you do is try the provided minimal examples related to ws client operation, eg,

https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/ws-client/minimal-ws-client-rx

should immediately receive ws data from libwebsockets.org.

fusionJose commented 5 years ago

Hi, I've tried with the minimal-ws-client-rx example. I included a writing when I get the LWS_CALLBACK_CLIENT_ESTABLISHED event previous to receive the response from server. But keep whithout receiving any data. It transmits the message but stops the program with no data received. I also deleted the use of SSL.

Here is the code:

`/*

include

include

include

include

include

include

static int interrupted, rx_seen, test; static struct lws *client_wsi;

static int callback_dumb_increment(struct lws wsi, enum lws_callback_reasons reason, void user, void *in, size_t len) { // The message we send back to the echo server const char msg[128] = "Simple webserver echo test!";

    // The buffer holding the data to send
    // NOTICE: data which is sent always needs to have a certain amount of memory (LWS_PRE) preserved for headers
    unsigned char buf[LWS_PRE + 128];

    // Allocating the memory for the buffer, and copying the message to it
    memset(&buf[LWS_PRE], 0, 128);
    strncpy((char*)buf + LWS_PRE, msg, 128);

    int n;

    json_object *jarray = json_object_new_array();
    json_object *jobj_type = json_object_new_object();
    json_object *jobj_ID = json_object_new_object();
    json_object *jobj_action = json_object_new_object();
    json_object *jobj = json_object_new_object();

    jobj_type = json_object_new_int(2); /* A call from client to server */

    jobj_ID = json_object_new_string("1946577");
    jobj_action = json_object_new_string("BootNotification");

    char *str = "{\
            \"chargePointVendor\": \"MilHouse\", \
            \"chargePointModel\": \"Fusion\", \
        }";

    //printf("str:\n---\n%s\n---\n\n", str);

    json_object_array_add(jarray,jobj_type);
    json_object_array_add(jarray,jobj_ID);
    json_object_array_add(jarray,jobj_action);

    jobj = json_tokener_parse(str);

    json_object_array_add(jarray,jobj);
    switch (reason) {

    /* because we are protocols[0] ... */
    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
            lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
                     in ? (char *)in : "(null)");
            client_wsi = NULL;
            break;

    case LWS_CALLBACK_CLIENT_ESTABLISHED:
            lwsl_user("%s: established\n", __func__);
            lws_callback_on_writable(wsi);
            break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:
        printf("[Test Protocol] The client is able to write.\n");

        printf("[Test Protocol] Writing \"%s\" to server.\n", msg);

        // Write the buffer from the LWS_PRE index + 128 (the buffer size)
        char ping[LWS_PRE + 250];
        int m;

        printf("jobj from str:\n---\n%s\n---\n", json_object_to_json_string_ext(jarray, JSON_C_TO_STRING_PLAIN));

        n = 0;

        n = lws_snprintf((char *)ping + LWS_PRE, 250,
            json_object_to_json_string_ext(jarray, JSON_C_TO_STRING_PLAIN));

        //sprintf((char *)ping + LWS_PRE, json_object_to_json_string_ext(jarray, JSON_C_TO_STRING_PLAIN));

        lwsl_user("Sending PING %d...\n", n);

        m = lws_write(wsi, ping + LWS_PRE, n, LWS_WRITE_TEXT);
        if (m < n) {
            lwsl_err("sending ping failed: %d\n", m);
        }
        break;

    case LWS_CALLBACK_CLIENT_RECEIVE:
            lwsl_user("RX: %s\n", (const char *)in);
            rx_seen++;
            if (test && rx_seen == 10)
                    interrupted = 1;
            break;

    case LWS_CALLBACK_CLIENT_CLOSED:
            client_wsi = NULL;
            break;

    default:
            break;
    }

    return lws_callback_http_dummy(wsi, reason, user, in, len);

}

static const struct lws_protocols protocols[] = { { "ocpp1.6", callback_dumb_increment, 0, 0, }, { NULL, NULL, 0, 0 } };

static void sigint_handler(int sig) { interrupted = 1; }

int main(int argc, const char *argv) { struct lws_context_creation_info info; struct lws_client_connect_info i; struct lws_context context; const char p; int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE / for LLL_ verbosity above NOTICE to be built into lws, lws

Thanks for your support

lws-team commented 5 years ago

.. mmm I don't really care about your project. So if something is wrong there, I don't want to stop the other things I am supposed to be doing to look into it.

I do care about my project. So please don't paste changed code, just tell me does the unchanged minimal example I pointed to connect to libwebsockets.org and get RX?

fusionJose commented 5 years ago

Yes, your unchanged minimal example receives data. I understand you, but the only thing i would want to know is the way to transmit a message before get RX. I´m not sure I am doing this well. I fell like the socket transmits de message and closes the socket, forgetting to listen to incoming data.

lws-team commented 5 years ago

Yes, your unchanged minimal example receives data.

OK, so I think there's no general problem about ws rx in lws as it is.

the only thing i would want to know is the way to transmit a message before get RX.

When the connection is ESTABLISHED, ask for a callback_on_writable(). In the WRITEABLE callback, do the lws_write() you want to do. If you want to do more, ask for a callback_on_writable() again.

I fell like the socket transmits de message and closes the socket, forgetting to listen to incoming data.

I don't know what side we're talking about but that's a decision for the code on that side when to close the ws connection. If something is wrong with what was transmitted to it, in terms of violating ws protocol, it might decide it should hang up... you should look at the logs and see what's happening there. If it;s just racing the other side, that's something you need to fix in your code.