Search code examples
cwebsocketcometd

noPoll Web socket library in C language always failing to connect


I'am trying to build a open source Cometd Library for C developers, to be able to run a client under barely any given device.

I manage to complete the handshake and data transfer with the longPolling technique, the next step is logically to give the option to the user to choose a Websocket connection.

To ease up the work I decided to use a library for that, I again fetched the internet and found two really relevant libraries:

  • The WSlay library, which seems to work well but doesn't run under OSX due to the epoll.h include which is not supported by apple.

  • The NoPoll library, which I think is the best one to use.

The problem is that I can't make it perform right, I always get a failed connections. After reading the full documentation I still don't understand why?

Here is my code:

    int main(void)
    {
      cometd        *cometd = cometd_new();
      JsonNode      *connect = json_node_new(JSON_NODE_OBJECT);
      noPollCtx     *ctx = nopoll_ctx_new(); //init the context of the lib
      noPollMsg     *msg; //variable to get messages

      if (!ctx) //check if the context is well inited
        puts("error ctx is nill");

      /*After the new you can enable or disable the Websocket simply do this, enabled by defautl*/
      cometd->config->webSockState = true;


      cometd_configure(cometd, COMETDOPT_URL, "http://m.zpush.ovh:8080/str/strd");
      cometd_configure(cometd, COMETDOPT_MAX_BACKOFF, 5000);
      struct _cometd_ext* logger = cometd_ext_logger_new();
      cometd_ext_add(&cometd->exts, logger);
      cometd_connect(cometd);
      connect = cometd_msg_connect_new(cometd);
      cometd_transport_send(cometd, connect); 
      //everything until here is for the handshake wich proceed well and the server returns a successful header

     //here i check if the user wants to Websocket upgrade his connection 
      if (cometd->config->webSockState == true)
        {
      // here i target the server
          noPollConn    *conn = nopoll_conn_new(ctx,  "54.171.156.38" , "8080" ,  "m.zpush.ovh:8080" , "ws://m.zpush.ovh:8080/str/strd", NULL, "null");
          if (!nopoll_conn_wait_until_connection_ready(conn, 5) ) 
      // here i check if the connection is ready
            {puts("nopoll_conn failed"); return (0);}
          if (nopoll_conn_send_text (conn, "hello how are you doing, do we connect ?", 40) != 40)
            {puts("send text just failed."); return(0);}
          else
            {
              while (nopoll_true) //the loop to receive and send messages
                {
                  msg = nopoll_conn_get_msg(conn);
                  if (msg)
                    break;

                  if (! nopoll_conn_is_ok (conn))
                    {
                      puts("connection failed during msg wait");
                      return nopoll_false;
                    }
                }
            }
        }
      cometd_subscribe(cometd, "/service/GmY-HuzW/6sd0/ls", handler);
      cometd_subscribe(cometd, "service/GmY-HuzW/6sd0/updateMeta", handler);
      cometd_subscribe(cometd, "/service/GmY-HuzW/6sd0/ls", handler);
      cometd_subscribe(cometd, "/service/GmY-HuzW/6sd0/newFile", handler);
      cometd_transport_send(cometd, cometd_ping_ls(cometd, "/service/GmY-HuzW/6sd0/ls"));

      cometd_listen(cometd);
      nopoll_ctx_unref(ctx);
      return 0;
    }

NoPoll implements a debug option, here is the result :

(proc 49413): (debug) nopoll_ctx.c:260 (nopoll_bool nopoll_ctx_register_conn(noPollCtx *, noPollConn *)) registered connection id 2, role: 0
(proc 49413): (debug) nopoll_conn.c:505 (noPollConn *__nopoll_conn_new_common(noPollCtx *, nopoll_bool, const char *, const char *, const char *, const char *, const char *, const char *)) Created noPoll conn-id=2 (ptr: 0x7f9d00501b60, context: 0x7f9d0041cdf0, socket: 3)
(proc 49413): (debug) nopoll_conn.c:284 (char *__nopoll_conn_get_client_init(noPollConn *)) Created Sec-WebSocket-Key nonce: Z0WLawAAAADGI3syAAAAAA==
(proc 49413): (debug) nopoll_conn.c:551 (noPollConn *__nopoll_conn_new_common(noPollCtx *, nopoll_bool, const char *, const char *, const char *, const char *, const char *, const char *)) Sending websocket client init: GET / HTTP/1.1
Host: 10.0.0.103
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Z0WLawAAAADGI3syAAAAAA==
Origin: http://10.0.0.103
Sec-WebSocket-Version: 13


(proc 49413): (critical) nopoll_conn.c:643 (noPollConn *__nopoll_conn_new_common(noPollCtx *, nopoll_bool, const char *, const char *, const char *, const char *, const char *, const char *)) ***Failed to send websocket init message, error code was: 57 (2), closing session***
(proc 49413): (debug) nopoll_conn.c:1036 (void nopoll_conn_shutdown(noPollConn *)) shutting down connection id=2 (session: 3, role: client)
(proc 49413): (debug) nopoll_conn.c:651 (noPollConn *__nopoll_conn_new_common(noPollCtx *, nopoll_bool, const char *, const char *, const char *, const char *, const char *, const char *)) Web socket initial client handshake sent

-> for better reading : Failed to send websocket init message, error code was: 57 (2), closing session

I really hope some one already successfully used the library and could give me some hints or guide lines. Any help would be very much appreciated, and will give me the ability to finish this library.

The code is still a little bit messy but if you want a full sight of my code I can upload to GitHub.

--> Update : I found a nasty trick to overcome this problem, i commented line N•845 nopoll_conn_shutdown(); in nopoll_conn.c method wich i think you have guessed it, belongs to the library. Then make install and no more error 57.


Solution

  • Working on your example, I found out that the remote server is closing the connection by reporting the following error:

    INFO: conn=0x1238500, conn-id=2, nopoll_conn_is_ok (0x1238500)=1, nopoll_conn_is_ready (0x1238500)=1...sending content
    ERROR: connection failed during msg wait, peer reported status=[1011] and reason=[java.text.ParseException: hello how are you doing, do we connect ?]
    

    That is, you are sending unexpected application format for this WebSocket, which is causing the connection close. However, noPoll's support for close frames with body (which includes a status code and a reason) wasn't supported.

    I've updated the library and now it is fully supported. You'll have to get a copy of the library from here (use SVN rev 227 at least).

    Along with that, here is a working updated example that goes to the point I'm talking about plus the couple of functions that will allow you get status and reason reported by remote peer:

    #include <nopoll.h>
    
    int main (void) {
    
        /* init the context of the lib */
        noPollCtx     * ctx = nopoll_ctx_new(); 
        /* variable to get messages */
        noPollMsg     * msg; 
        noPollConn    * conn;
    
        /* nopoll_log_enable (ctx, nopoll_true);
           nopoll_log_color_enable (ctx, nopoll_true); */
    
        if (!ctx) {
            puts("error ctx is nill");
            return -1; /* do not continue */
        }
    
        /* here i target the server */
        conn = nopoll_conn_new (ctx,  "54.171.156.38" , 
                    "8080" , 
                    "m.zpush.ovh:8080" , 
                    "ws://m.zpush.ovh:8080/str/strd", 
                    NULL, "null");
        if (!nopoll_conn_wait_until_connection_ready (conn, 5) )  {
            /* here i check if the connection is ready */
            puts ("nopoll_conn failed"); 
            return -1;
        } /* end if */
    
        printf ("INFO: conn=%p, conn-id=%d, nopoll_conn_is_ok (%p)=%d, nopoll_conn_is_ready (%p)=%d...sending content\n",
            conn, nopoll_conn_get_id (conn), conn, 
            nopoll_conn_is_ok (conn), conn, 
            nopoll_conn_is_ready (conn));
    
        if (nopoll_conn_send_text (conn, "hello how are you doing, do we connect ?", 40) != 40) {
            puts("send text just failed."); 
            return - 1;
        } /* end if */
    
        while (nopoll_true) {
    
            /* the loop to receive and send messages */
            msg = nopoll_conn_get_msg(conn);
            if (msg)
                break;
    
            if (! nopoll_conn_is_ok (conn)) {
                printf ("ERROR: connection failed during msg wait, peer reported status=[%d] and reason=[%s]\n",
                    nopoll_conn_get_close_status (conn), 
                    nopoll_conn_get_close_reason (conn));
    
                return nopoll_false;
            }
        }
    
        /* close connection always */
        nopoll_conn_close (conn);
        /* release context */
        nopoll_ctx_unref (ctx);
        return 0;
    }
    

    Best Regards,