C++ Sockets cannot connect

2,428 views
Skip to first unread message

Treyten Carey

unread,
Jul 24, 2018, 11:11:06 AM7/24/18
to emscripten-discuss
I am porting my C++ application, which uses sockets, to HTML with Emscripten.

Here is the code I took from a blog post in order to test different ways of connecting:
void connect(const std::string& host, int port) {
    int sock;
    bool connected;

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock == -1) {
        Error("connect(): failed to create socket");
        return;
    }
    fcntl(sock, F_SETFL, O_NONBLOCK);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if(inet_pton(AF_INET, host.c_str(), &addr.sin_addr) != 1) {
        Error("connect(): inet_pton failed");
        return;
    }

    const int res = ::connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    if(res == -1) {
        if(errno == EINPROGRESS) {
            Debug("connect(): Connection in progress");

            /* Wait for connection to complete */
            fd_set sockets;
            FD_ZERO(&sockets);
            FD_SET(_data->socket, &sockets);

            /* You should probably do other work instead of busy waiting on this...
               or set a timeout or something */
            while(select(sock + 1, nullptr, &sockets, nullptr, nullptr) <= 0) { Debug(std::to_string(errno)); }

            connected = true;

        } else {
            Error("Socket::connect(): connection failed");
            return;
        }
    } else {
        connected = true;
    }
}

The socket gets stuck in an infinite loop of errno 115 (EINPROGRESS).

My server successfully accept()'s the connection, but the Emscripten client cannot receive/send any data. If I don't check for select, the network console shows OK 200, but sending data results in "Error during WebSocket handshake: net::ERR_INVALID_HTTP_RESPONSE" (probably because without select(), it doesn't wait for EINPROGRESS to finish before trying to send).

Has anyone successfully got sockets to work with Emscripten?

Floh

unread,
Jul 24, 2018, 12:53:54 PM7/24/18
to emscripten-discuss
I think you need to check for a couple more return codes from connect(), at least that's what I'm doing:

((errno == EINPROGRESS) || (errno == EWOULDBLOCK) || (errno == EISCONN))

Also see this thread:


And this gist which contains the code for a simple cross-platform socket client (connects to a TCP socket on native platforms, and a WebSocket on emscripten), the client might look a bit unfamiliar because it basically a state machine which has a 'desired state' (connected or disconnected), and handles all the state transition details as well as sending and receiving in the per-frame-update:


Hope this helps!
-Floh.

Treyten Carey

unread,
Jul 24, 2018, 2:22:33 PM7/24/18
to emscripten-discuss
I copied the code from gist, it didn't seem to work.

Here's my code, which is equivalent:
void netClient()
{
    bool connected = false;
    int sock;

    // create socket
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == -1)
        printf("SOCKERR\n");

    fcntl(sock, F_SETFL, O_NONBLOCK);

    std::string hostName = "127.0.0.1";
    const uint16_t port = 2320;

    sockaddr_in addr;
    // Memory::Clear(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    uint32_t ipAddr = 0;

    ipAddr = inet_addr(hostName.c_str());

    addr.sin_addr.s_addr = ipAddr;
    printf("Connecting to '%s' => '%d.%d.%d.%d', port %d\n",
        hostName.c_str(),
        ipAddr & 0xFF,
        (ipAddr >> 8) & 0xFF,
        (ipAddr >> 16) & 0xFF,
        (ipAddr >> 24) & 0xFF,
        port);

    const int connectRes = connect(sock, (sockaddr*)&addr, sizeof(addr));
    if (-1 == connectRes) {
        if (((errno == EINPROGRESS) || (errno == EWOULDBLOCK) || (errno == EISCONN))) {
            connected = true;
        }
    }
    if (!connected)
        printf("Failed to connect to '%s:%d'\n", hostName.c_str(), port);

    fd_set fdr, fdw;
    FD_ZERO(&fdr);
    FD_ZERO(&fdw);
    FD_SET(sock, &fdr);
    FD_SET(sock, &fdw);

    // for windows, it is necessary to provide an empty timeout
    // structure, in order for select() to not block
    struct timeval tv = { };
    const int selectRes = select(int(sock+1), &fdr, &fdw, 0, &tv);
    if (selectRes == -1) {
        printf("select() failed.\n");
    }
    else if (selectRes > 0) {
        printf("Try\n");
        if (FD_ISSET(sock, &fdr)) {
            printf("Should Recv OK!\n");
        }
        if (FD_ISSET(sock, &fdw)) {
            printf("Should Send OK!\n");
        }
    }
    printf("selectedRes: %d\n", selectedRes);
}

The Emscripten console outputs the following:
Connecting to '127.0.0.1' => '127.0.0.1', port 2320 
 selectedRes: 0 

The developer console outputs the following:
WebSocket connection to 'ws://127.0.0.1:2320/' failed: Error during WebSocket handshake: net::ERR_INVALID_HTTP_RESPONSE

If I use port 80 instead of 2320, I then get:
WebSocket connection to 'ws://127.0.0.1/' failed: Error during WebSocket handshake: Invalid status line 

Floh

unread,
Jul 25, 2018, 4:08:50 AM7/25/18
to emscripten-discuss
This looks like a protocol mismatch between the browser and websocket server...

I remember that we had similar problems with some combinations of browsers and websocket proxies like websockify.js (I think it was something about subprotocol-negotiation, where Chrome expected a specific subprotocol not supported by websockify.js, or at least something along those lines: https://hpbn.co/websocket/#subprotocol-negotiation).

In the end that was the reason why we used the Gorilla Websocket module instead of a separate proxy process, it was the only solution we tested that worked with all browsers. But this was end of 2016, not sure if things have improved or regressed since then ;)

PS: you *are* connecting to a WebSocket-compatible port on the server-side do you? It isn't quite clear from your posts, but a normal TCP socket port as you use for a 'normal' TCP client won't work, the port needs to implement the WebSocket protocol (basically start as HTTP port, which switches over to the WebSocket protocol, which is very different from TCP (for instance it's message-oriented, not stream-oriented).

Cheers!
-Floh.

Treyten Carey

unread,
Jul 25, 2018, 3:30:04 PM7/25/18
to emscripten-discuss
Thanks, I converted the server to a WebSocket compatible version using cppWebSockets with libwebsockets as an example and that was what I needed.
Appreciate your help.

Treyten Carey

unread,
Aug 7, 2018, 4:11:34 PM8/7/18
to emscripten-discuss
Hi again,

My server is now using Websocketpp to connect to Emscripten's regular sockets.
They communicate back and forth just fine on Firefox and Edge.

However, in Chrome, I get the following error:
WebSocket connection to 'ws://127.0.0.1:2321/' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

Is this an Emscripten thing or a Websocketpp thing? I couldn't find any viable solutions online.

Treyten Carey

unread,
Aug 9, 2018, 8:40:09 AM8/9/18
to emscripten-discuss
Also, I can't seem to connect over http. I get the error

"The page was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint. This request has been blocked; this endpoint must be available over WSS."

I read that I can turn regular sockets into secure sockets with OpenSSL, but I am unable to build OpenSSL for Emscripten.
See here: https://github.com/kripken/emscripten/issues/6970

Floh

unread,
Aug 11, 2018, 8:13:45 AM8/11/18
to emscripten-discuss
I think this is the same issue as described here: https://github.com/kripken/emscripten/pull/6960

Basically, if your app is served via https, you need to tell the emscripten socket emulation that it needs to use wss:// instead of ws:// as WebSocket protocol (and your WebSocket backend must be able to deal with wss://).

Treyten Carey

unread,
Aug 13, 2018, 2:18:00 PM8/13/18
to emscripten-discuss
Great, that's exactly what I needed. Thank you.
Reply all
Reply to author
Forward
0 new messages