Added support for multiple listeners to DNS multicast datagrams.
authorRasmus Andersson <rasmus@notion.se>
Wed, 11 Aug 2010 21:03:56 +0000 (23:03 +0200)
committerRyan Dahl <ry@tinyclouds.org>
Thu, 12 Aug 2010 02:02:20 +0000 (19:02 -0700)
Some platforms require SO_REUSEPORT to be set for the socket.

src/node_net.cc
test/simple/test-dgram-multicast.js [new file with mode: 0644]

index 6bf9416..8d920c9 100644 (file)
@@ -136,6 +136,9 @@ static Handle<Value> Socket(const Arguments& args) {
   // default to TCP
   int domain = PF_INET;
   int type = SOCK_STREAM;
+#ifdef SO_REUSEPORT
+  bool set_reuseport = false;
+#endif
 
   if (args[0]->IsString()) {
     String::Utf8Value t(args[0]->ToString());
@@ -158,12 +161,21 @@ static Handle<Value> Socket(const Arguments& args) {
     } else if (0 == strcasecmp(*t, "UDP")) {
       domain = PF_INET;
       type = SOCK_DGRAM;
+#ifdef SO_REUSEPORT
+      set_reuseport = true;
+#endif
     } else if (0 == strcasecmp(*t, "UDP4")) {
       domain = PF_INET;
       type = SOCK_DGRAM;
+#ifdef SO_REUSEPORT
+      set_reuseport = true;
+#endif
     } else if (0 == strcasecmp(*t, "UDP6")) {
       domain = PF_INET6;
       type = SOCK_DGRAM;
+#ifdef SO_REUSEPORT
+      set_reuseport = true;
+#endif
     } else {
       return ThrowException(Exception::Error(
             String::New("Unknown socket type.")));
@@ -180,6 +192,16 @@ static Handle<Value> Socket(const Arguments& args) {
     return ThrowException(ErrnoException(fcntl_errno, "fcntl"));
   }
 
+#ifdef SO_REUSEPORT
+  // needed for datagrams to be able to have multiple processes listening to
+  // e.g. broadcasted datagrams.
+  if (set_reuseport) {
+    int flags = 1;
+    setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const char *)&flags,
+               sizeof(flags));
+  }
+#endif
+
   return scope.Close(Integer::New(fd));
 }
 
diff --git a/test/simple/test-dgram-multicast.js b/test/simple/test-dgram-multicast.js
new file mode 100644 (file)
index 0000000..75c4443
--- /dev/null
@@ -0,0 +1,75 @@
+common = require("../common");
+assert = common.assert
+
+var dgram = require("dgram"),
+    sys = require('sys'),
+    assert = require('assert'),
+    Buffer = require("buffer").Buffer;
+var timeoutTimer;
+var LOCAL_BROADCAST_HOST = '224.0.0.1';
+var sendMessages = [
+  new Buffer("First message to send"),
+  new Buffer("Second message to send"),
+  new Buffer("Third message to send"),
+  new Buffer("Fourth message to send")
+];
+var listenSockets = [];
+var sendSocket = dgram.createSocket('udp4')
+  .on('close', function () { console.log('sendSocket closed'); })
+  .on('error', function (err) { throw err; });
+sendSocket.setBroadcast(true);
+var i = 0;
+sendSocket.sendNext = function (){
+  sendSocket.started = true;
+  var buf = sendMessages[i++];
+  if (!buf) {
+    try { sendSocket.close(); }catch(e){}
+    listenSockets.forEach(function (sock) { sock.close(); });
+    clearTimeout(timeoutTimer);
+    return;
+  }
+  sendSocket.send(buf, 0, buf.length, common.PORT, LOCAL_BROADCAST_HOST,
+      function (err) {
+    if (err) throw err;
+    console.log('sent %s to %s', sys.inspect(buf.toString()),
+      LOCAL_BROADCAST_HOST+common.PORT);
+    process.nextTick(sendSocket.sendNext);
+  });
+}
+
+function mkListener() {
+  var receivedMessages = [];
+  var listenSocket = dgram.createSocket('udp4')
+    .on('message', function(buf, rinfo) {
+      console.log('received %s from %j', sys.inspect(buf.toString()), rinfo);
+      receivedMessages.push(buf);
+    })
+    .on('close', function () {
+      console.log('listenSocket closed -- checking received messages');
+      var count = 0;
+      receivedMessages.forEach(function(buf){
+        for (var i=0; i<sendMessages.length; ++i) {
+          if (buf.toString() === sendMessages[i].toString()) {
+            count++;
+            break;
+          }
+        }
+      });
+      assert.strictEqual(count, sendMessages.length);
+    })
+    .on('error', function (err) { throw err; })
+    .on('listening', function() {
+      if (!sendSocket.started) {
+        sendSocket.started = true;
+        process.nextTick(function(){ sendSocket.sendNext(); });
+      }
+    })
+  listenSocket.bind(common.PORT);
+  listenSockets.push(listenSocket);
+}
+
+mkListener();
+mkListener();
+mkListener();
+
+timeoutTimer = setTimeout(function () { throw new Error("Timeout"); }, 500);