1

I want to implement a zmq communication between a docker container and the localhost of my windows / mac computer. I want both the docker container and the localhost to be able to send and receive messages.

On linux, the following files enable the communication to work but not on Windows (WSL) / Mac :

Dockerfile :

FROM python:3.7

COPY requirements.txt ./

# COPY ./Received_Pictures ./

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5546

CMD [ "python", "./reqrep_server.py", "5546" ]

reqrep_server.py :

import zmq
import time
import sys

port = "5546"
if len(sys.argv) > 1:
    port = sys.argv[1]
    int(port)

context = zmq.Context()
socket = context.socket(zmq.REP)
# socket.bind("tcp://*:%s" % port)
socket.bind("tcp://127.0.0.1:%s" % port)

while True:
    #  Wait for next request from client
    message = socket.recv()
    print("Received request: ", message)
    time.sleep(1)
    socket.send_string("World from %s" % port)

And the client file client.py :

import zmq
import sys

port = "5546"
if len(sys.argv) > 1:
    port = sys.argv[1]
    int(port)

if len(sys.argv) > 2:
    port1 = sys.argv[2]
    int(port1)

context = zmq.Context()
print("Connecting to server...")
socket = context.socket(zmq.REQ)
socket.connect("tcp://127.0.0.1:%s" % port)
if len(sys.argv) > 2:
    socket.connect("tcp://localhost:%s" % port1)


#  Do 10 requests, waiting each time for a response
for request in range(1, 10):
    print("Sending request ", request, "...")
    socket.send_string("Hello")
    #  Get the reply.
    message = socket.recv()
    print("Received reply ", request, "[", message, "]")

Then I run the following commands :

docker build -t server . --network=host

docker container run --network=host server

And in another terminal:
python client.py

But no message is either sent or received by the Docker container nor the localhost. On Windows, when I do the following modifications :

  • Inside reqrep_server.py : socket.connect (" tcp:// *:%s" % port)
  • When building the docker container : I remove the “--host=network” flag then I run docker container run -p 5546:5546 server.

It works but only in one direction : the docker container sending to the localhost but not in the other direction, the docker container does not receive the “Hello” message but is able to send “World from …”.

Do you have any idea why it doesn’t work ?

1 Answer 1

2

Each container is its own "localhost", which is separate from the host machine's "localhost", what an end user's browser might believe is "localhost", and so on.

You are running the server in a container, which is the easy case. Servers in containers generally must listen on 0.0.0.0 or "all interfaces". If they listen or bind to 127.0.0.1, that's the container-private localhost interface, and the container will be unreachable from outside its own container. Also see Docker app server ip address 127.0.0.1 difference of 0.0.0.0 ip.

# reqrep_server.py
socket.bind("tcp://0.0.0.0:%s" % port)
#                  ^^^^^^^ not 127.0.0.1

When you start the container, you need a docker run -p (or Compose ports:) setting to make the port visible outside of Docker space. The first port number is the port you'll use to call the container, and the second port number must match what the server.bind() call is using. These may match but they don't have to.

You should not normally use --net=host. It generally disables Docker's networking functionality, which means you won't have an option to remap ports or to hide ports from the host system, and you can't do things like communicate with other containers using their container names as host names. On VM-based setups like Docker Desktop (you mention MacOS and Windows hosts) the "host network" is often the VM's, so you won't be able to reach the container even with host networking.

docker build -t server .

docker run -p 5546:5546 server
#          ^^^^^^^^^^^^ add -p option, remove --network=host

Now your non-container client can connect to the host's IP address and the first docker run -p port number. If the client is on the same machine as Docker and either you're directly using Docker on a native-Linux host or you're using Docker Desktop, then you can use localhost or 127.0.0.1 here. More generally, the docker run -p option makes it look like the host is running the server process, and you'd connect to it the same way you would if the process weren't running in a container.

socket.connect("tcp://127.0.0.1:%s" % port)
#                     ^^^^^^^^^ correct on native Docker or Docker Desktop
#                               and only from the same machine
#                               and not calling from a container
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your answer, now it works on the client side ("World from {port}" is received and printed) However, I still don't see the received message in the docker container ("Hello"). Do you have any idea why ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.