NolanRudolph
NolanRudolph

Reputation: 126

Host to Docker Container Remote Socket Failure

I'm trying to communicate a simple message between my host machine and my docker container.

I began by creating my docker container with the host network driver:
docker run -t -d --network host --name cfalcon ubuntu

Next I entered the container:
docker exec -it cfalcon

I identified my host IP as 192.168.0.109 and my container's IP as 172.17.0.1 using:
hostname -I

I made a file at ~/Documents/tcp_server.c in my container with the following code:

TCP_SERVER.C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h>

#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char* argv[]) {

    char address[] = "192.168.0.109";
    // Creating Server Socket
    int server_socket;
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    // Defining Server Address
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    // Specify the port, 9001 seems safe
    server_address.sin_port = htons(9001);
    server_address.sin_addr.s_addr = inet_addr(address);

    // Binding our Socket to our Specified IP, 192.168.0.109
    bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));

    // Listen() Function Begins Listening for a Connection
    listen(server_socket, 1);

    // Ability to Accept Connections
    int client_socket;
    client_socket = accept(server_socket, NULL, NULL);

    char server_message[256] = "Its-a me, a-mario!";

    // Send a message with send()
    send(client_socket, server_message, sizeof(server_message), 0);

    close(server_socket);

    return 0;
}

Returning to my host machine, I decided to make a new file at ~/Documents/tcp_client.c with the following code:

TCP_CLIENT.C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h>

#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char* argv[]) {
    char address[] = "172.17.0.1";
    // Creating a Socket
    int network_socket;
    // socket(domain of socket (generally AF_INET, a constant), TCP socket, protocol)
    network_socket = socket(AF_INET, SOCK_STREAM, 0);

    // Specifying an Address for the Socket (Address family is same as parameter one of socket())
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;

    // Specify the port, 9001 seems safe
    server_address.sin_port = htons(9001);
    server_address.sin_addr.s_addr = inet_addr(address);

    // Returns an integer for error handling
    int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));
    // Error Handling
    if (connection_status == -1) {
        printf("There was an error making a connection to the remote socket.\n\n");
    }

    // If we send data, we receive a response from the server in recv
    char server_response[256];
    recv(network_socket, &server_response, sizeof(server_response), 0);

    // Printing the Server's Response
    printf("The server responded with: %s\n", server_response);

    // Avoiding data leaks
    close(network_socket);

    return 0;
}

Unfortunately, I did not receive "Its-a me, a-Mario!", but rather "There was an error making a connection to the remote socket".

I believed I perfectly linked the two using respective IP's and identical port numbers, but it would appear I haven't. Please let me know if you have any guidance toward my predicament.

Edit: Update! I installed Netcat on my container and established a connection between my host and my container.
Host: $ nc -l 9001
Container: $ nc localhost 9001
Any message sent from the container in the following prompt reaches my host machine, which tells me this is solely an error with my code.

Upvotes: 0

Views: 1147

Answers (1)

David Maze
David Maze

Reputation: 159722

At the lowest level, your server code bind(2) to 192.168.0.9, but your client code connect(2) to 172.17.0.1. The server will only accept connections to the single address it's bound to. If you're using Docker host networking, the server process is indistinguishable from any other process running on the host from a network point of view, and while 172.17.0.1 will presumably reach the host as well, since it's not exactly the same address, your connection will be refused.

If the server is running in a Docker container, and the client is running directly on the same host:

  1. bind(2) to 0.0.0.0. (Binding to 127.0.0.1 will make the process unreachable from outside the container; the container-private IP address is unpredictable.)
  2. Do not start the container with --net host. Do start it with an appropriate -p option mapping the container port to some host port; for example, -p 9001:9001.
  3. From the client, running not in Docker, on the same host, connect(2) to 127.0.0.1 port 9001. From a different host (Docker or otherwise), use the first host's name.

If the client is running in a different container on the same host:

  1. bind(2) to 0.0.0.0.
  2. docker create network something. You don't need any additional arguments, but you do need to create the network.
  3. When you docker run both containers, start them with --net something matching the network you previously created, and a useful --name.
  4. In the client, use getaddrinfo(3) to look up the server's container name; then connect(2) to that sockaddr.

Note that neither of these paths involve looking up the container-private IP address (except as a low-level detail in an ordinary "connect to this hostname" path). This changes routinely (whenever you delete and recreate a container), isn't accessible from off-host, and isn't even accessible from the host console on some Docker setups. There's never a need to do look this up.

Also note that neither path involves docker exec. It's a useful debugging tool but it isn't usually part the core container workflow. (If you find yourself "docker run a container and then immediately docker exec", generally you should change the container startup sequence to do whatever the docker exec thing is itself.)

Upvotes: 1

Related Questions