Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple binds on DGRAM socket #4434

Open
sphaero opened this issue Oct 5, 2022 · 7 comments
Open

Multiple binds on DGRAM socket #4434

sphaero opened this issue Oct 5, 2022 · 7 comments

Comments

@sphaero
Copy link
Contributor

sphaero commented Oct 5, 2022

Issue description

Multiple binds on a DGRAM socket only work for the first bind

Environment

  • libzmq version: master ( 875c2b1)
  • OS: Debian testing

Minimal test code / Steps to reproduce the issue

#include "zmq.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

int main (void)
{
  void *context = zmq_ctx_new ();
  void *data_socket = zmq_socket (context, ZMQ_DGRAM);

  int rc = zmq_bind(data_socket, "udp://*:1511");
  assert(rc != -1);
  if (rc == -1)
  {
      printf ("E: bind failed: %s\n", strerror (errno));
      return 1;
  }

  rc = zmq_bind(data_socket, "udp://*:6200");
  if (rc == -1)
  {
      printf ("E: bind failed: %s\n", strerror (errno));
      return 1;
  }
  assert (rc == 0);

  int count = 0;
  printf("starting recv\n");
  while (count < 10) {
    char buffer [100];
    rc = zmq_recv (data_socket, buffer, 100, 0);
    assert(rc > 0);
    printf ("Received Hello %.*s\n", 100, buffer);
    count++;
  }

What's the actual result? (include assertion message & call stack if applicable)

There's no error. Just that no data is being received on port 6200

What's the expected result?

Both ports would receive data

@sphaero
Copy link
Contributor Author

sphaero commented Oct 5, 2022

Netstat also only shows it's listening on port 1511

netstat -anp | grep czmq
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
udp        0      0 0.0.0.0:1511            0.0.0.0:*                           151114/czmqtests    

@sphaero
Copy link
Contributor Author

sphaero commented Oct 5, 2022

probably related: #4342

@sphaero
Copy link
Contributor Author

sphaero commented Oct 5, 2022

Just after

add_endpoint (make_unconnected_bind_endpoint_pair (endpoint_uri_),

in socket_base.cpp the second udp socket is created

udp        0      0 0.0.0.0:1511            0.0.0.0:*                           162018/czmqtests
udp        0      0 0.0.0.0:6200            0.0.0.0:*                           162018/czmqtests

However a few steps further in execution it is gone again. It's gone once zmq_recv is called in the socket. But not sure whether that's related as it is probably an async operation in a background thread that handles the socket

@sphaero
Copy link
Contributor Author

sphaero commented Jan 31, 2023

Still tracing this. After creation of the second socket (second bind) I can see a socket is being created. (fd=10, first bind fd=9). However after creation I also see term command (pipe_term_ack) happening which calls the destructor of the udp_engine_t instance. So the socket gets destroyed again.

@sphaero
Copy link
Contributor Author

sphaero commented Jan 31, 2023

I'm receiving an attach command for the second bind here (level 10 in stacktrace):

cmd.destination->process_command (cmd);

Which results in reading a 'delimiter' on the pipe which triggers the destruction (level 2):

send_pipe_term_ack (_peer);

1  zmq::object_t::send_pipe_term_ack         object.cpp       352 0x7f9e80ab954e 
2  zmq::pipe_t::process_delimiter            pipe.cpp         511 0x7f9e80abdbb1 
3  zmq::pipe_t::read                         pipe.cpp         221 0x7f9e80abddae 
4  zmq::session_base_t::pull_msg             session_base.cpp 173 0x7f9e80ac755b 
5  zmq::udp_engine_t::out_event              udp_engine.cpp   440 0x7f9e80ae78db 
6  zmq::udp_engine_t::restart_output         udp_engine.cpp   527 0x7f9e80ae7c78 
7  zmq::udp_engine_t::plug                   udp_engine.cpp   218 0x7f9e80ae8030 
8  zmq::session_base_t::process_attach       session_base.cpp 418 0x7f9e80ac7ed3 
9  zmq::object_t::process_command            object.cpp       96  0x7f9e80ab9094 
10 zmq::io_thread_t::in_event                io_thread.cpp    91  0x7f9e80ab191f 
11 zmq::epoll_t::loop                        epoll.cpp        210 0x7f9e80ab0535 
12 zmq::worker_poller_base_t::worker_routine poller_base.cpp  146 0x7f9e80ac04ac 
13 thread_routine                            thread.cpp       256 0x7f9e80ad7a22 
14 start_thread                              pthread_create.c 442 0x7f9e808b2fd4 
15 clone3                                    clone3.S         81  0x7f9e8093366c 

Order of commands handled internally are:

plug (*:1511)
own (*:1511)
own (*:6200)
pipe_term
plug (*:6200)
attach (*:1511)
attach (*:6200)
pipe_term_ack
pipe_term_ack (here the desctruction of the second socket gets initiated)
term_req (*:6200)
term (*:6200)
term_ack 

@sphaero sphaero closed this as completed Jan 31, 2023
@sphaero sphaero reopened this Jan 31, 2023
@sphaero
Copy link
Contributor Author

sphaero commented Jan 31, 2023

As a reference when I change to TCP using a sub socket I get only the following commands:

plug
own
plug
own

Then it runs just fine

@sphaero
Copy link
Contributor Author

sphaero commented Jan 31, 2023

Ok, it's a feature as the destroy is initiated here:

libzmq/src/dgram.cpp

Lines 60 to 66 in ce6d48c

// ZMQ_DGRAM socket can only be connected to a single peer.
// The socket rejects any further connection requests.
if (_pipe == NULL)
_pipe = pipe_;
else
pipe_->terminate (false);
}

Which clearly says:

ZMQ_DGRAM socket can only be connected to a single peer.
The socket rejects any further connection requests.

So instead of this being a bug it might be a feature request. Question then is whether this limitations serves a purpose or not?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant