Home > Blockchain >  C sockets: accept() hangs when client calls connect(), but accept() responds to HTTP GET request
C sockets: accept() hangs when client calls connect(), but accept() responds to HTTP GET request

Time:12-15

I'm trying to write a demo server/client program in C . I first ran my server program on my Macbook, with ngrok on and forwarding a public address on the Internet to a local address on my machine. I'm seeing something that I don't understand when I try to run my client program to connect to the server:

  1. If the client tries to connect to localhost at the local port defined in the server program, the server accepts as expected and the client successfully connects,
  2. If the client tries to connect to the ngrok server address at port 80 (default for ngrok), then the client connects, but the server is still blocked at the accept call. (This I don't understand!)
  3. If I send an HTTP GET request to the ngrok server address, the server successfully accepts the connection.

Why do I see these? In the ideal case, I want my server to accept connections from my client program, not just respond to the HTTP GET request.

Here's my code if that helps: For the client,

#include "helpers.hh"
#include <cstdio>
#include <netdb.h>

// usage: -h [host] -p [port]
int main(int argc, char** argv) {
    const char* host = "x.xx.xx.xx"; // use the server's ip here.
    const char* port = "80";

    // parse arguments
    int opt;
    while ((opt = getopt(argc, argv, "h:p:")) >= 0) {
        if (opt == 'h') {
            host = optarg;
        } else if (opt == 'p') {
            port = optarg;
        }
    }

    // look up host and port
    struct addrinfo hints, *ais;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;        // use IPv4 or IPv6
    hints.ai_socktype = SOCK_STREAM;    // use TCP
    hints.ai_flags = AI_NUMERICSERV;
    if (strcmp(host, "ngrok") == 0) {
        host = "xxxx-xxxx-xxxx-1011-2006-00-27b9.ngrok.io";
    }
    int r = getaddrinfo(host, port, &hints, &ais);
    if (r != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
        exit(1);
    }

    // connect to server
    int fd = -1;
    for (auto ai = ais; ai && fd < 0; ai = ai->ai_next) {
        fd = socket(ai->ai_family, ai->ai_socktype, 0);
        if (fd < 0) {
            perror("socket");
            exit(1);
        }

        r = connect(fd, ai->ai_addr, ai->ai_addrlen);
        if (r < 0) {
            close(fd);
            fd = -1;
        }
    }
    if (fd < 0) {
        perror("connect");
        exit(1);
    }
    freeaddrinfo(ais);

    // 
    printf("Connection established at fd %d\n", fd);
    FILE* f = fdopen(fd, "a ");
    fwrite("!", 1, 1, f);
    fclose(f);
    while (true) {
    }
    
}

And for the server:

#include "helpers.hh"

void handle_connection(int cfd, std::string remote) {
    (void) remote;
    printf("Received incoming connection at cfd: %d\n", cfd);
    usleep(1000000);
    printf("Exiting\n");
}


int main(int argc, char** argv) {
    int port = 6162;
    if (argc >= 2) {
        port = strtol(argv[1], nullptr, 0);
        assert(port > 0 && port <= 65535);
    }

    // Prepare listening socket
    int fd = open_listen_socket(port);
    assert(fd >= 0);
    fprintf(stderr, "Listening on port %d...\n", port);

    while (true) {
        struct sockaddr addr;
        socklen_t addrlen = sizeof(addr);

        // Accept connection on listening socket
        int cfd = accept(fd, &addr, &addrlen);
        if (cfd < 0) {
            perror("accept");
            exit(1);
        }

        // Handle connection
        handle_connection(cfd, unparse_sockaddr(&addr, addrlen));
    }
}

CodePudding user response:

Contrary to the typical port forwarding done in the local router, ngrok is not a port forwarder at the transport level (TCP) but it is a request forwarder at the HTTP level.

Thus if the client does a TCP connect to the external ngrok server nothing will be forwarded yet. Only after the client has send the HTTP request the destination will be determined and then this request will be send to the ngrok connector on the internal machine, which then will initiate the connection to the internal server and forward the request.

  • Related