3

I've been trying to somewhat reverse engineer a project to discover Logitech Harmony Hub devices on my network, and posted this question to see if someone could help me understand UDP broadcast. The answer explained that I've implementing the send portion of the UDP broadcast, but I've not implemented anything to "listen" for responses. And that's where I'm struggling. Here's my send code;

import UIKit
import CocoaAsyncSocket

class ViewController: UIViewController, GCDAsyncUdpSocketDelegate {

var address = "255.255.255.255"
var port:UInt16 = 5224
var socket:GCDAsyncUdpSocket!
var socketReceive:GCDAsyncUdpSocket!
var error : NSError?

override func viewDidLoad() {
super.viewDidLoad()

let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)

socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)

    do {
        try socket.enableBroadcast(true)
    } catch {
        print(error)
    }

}

func udpSocket(sock: GCDAsyncUdpSocket!, didConnectToAddress address: NSData!) {
    print("didConnectToAddress");
}

func udpSocket(sock: GCDAsyncUdpSocket!, didNotConnect error: NSError!) {
    print("didNotConnect \(error)")
}

func udpSocket(sock: GCDAsyncUdpSocket!, didSendDataWithTag tag: Int) {
    print("didSendDataWithTag")
} 

func udpSocket(sock: GCDAsyncUdpSocket!, didNotSendDataWithTag tag: Int, dueToError error: NSError!) {
    print("didNotSendDataWithTag")
}

func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {

var host: NSString?
var port1: UInt16 = 0
GCDAsyncUdpSocket.getHost(&host, port: &port1, fromAddress: address)
print("From \(host!)")

let gotdata: NSString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
print(gotdata)

    }

}

I see I have the code to handle the response (in didReceiveData), but I'm unsure what I need to implement to get the listening going;

  • Do I need to "bind" to the listener port (in this case, 61991)?
  • Do I need to "join the multicast group"? And if so, at what address? I tried doing so at "255.255.255.255", which creates a setSocketOpt() error when I build.
  • I know I need to call beginReceiving(), and can I do all this on socket, or do I need to instantiate a separate socket for the listening?

Edit: Resolved

The below answer absolutely helped me to solve the problem. It seemed I wasn't getting a response because I had not fully implemented a means of handling the incoming response.

Per the code provided in the answer below, I added the following;

// Setup the other socket (used to handle the response from the Harmony hub)
    otherSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())

    do {

        // Accept connections on port 61991
        try otherSocket.acceptOnPort(61991)

    } catch {

        // Handle any errors here
        print(error)
    }

I also set this controller to be a GCDAsyncSocketDelegate, which seemed to do the trick. I was able to read the response in didReadData.

Community
  • 1
  • 1
ZbadhabitZ
  • 2,753
  • 1
  • 25
  • 45

1 Answers1

3

The following code changes enabled me to receive UDP packets that I sent from my Mac using netcat, but my Harmony hub didn't seem to send anything, so I am not sure if the data that is being sent is correct.

override func viewDidLoad() {
super.viewDidLoad()

let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)

socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())


    do {
        try self.socket.bindToPort(61991)
        try self.socket.beginReceiving()
        try socket.enableBroadcast(true)
        socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)
    } catch {
        print(error)
    }

}

From the command line you can test receiving using the command

echo -n "hello" | nc -4u -w1 x.x.x.x 61991
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Hi @Paulw11 - Thank you for clarifying this. Using your code, I am also able to receive messages sent via Terminal, and receive them on the specific port in my app. I have the same experience with my Harmony Hub. As a test, I did `try self.socket.bindToPort(5224)` in my app, and used the Harmony app on my iPhone/same wifi network to pretend to find a new hub. It did send out a message in the form `_logitech-reverse-bonjour._tcp.local.\nXXXXX`, though XXXXX varied to a different port each time. – ZbadhabitZ Feb 16 '16 at 16:47
  • Also, @Paulw11 - I'm actually finding that the NODE.JS project I'm trying to base this project on is now unable to find my already established Harmony Hub (it was able to last week). I'm unsure of the exact steps but it's almost as if running the NODE.JS project lets it find the hub once, and then the hub no longer responds to UDP broadcasts until it is reset (unplugged and plugged back in). I am still trying to figure that out. – ZbadhabitZ Feb 16 '16 at 17:10
  • Following up on this, I now realize that the iOS app is not receiving the response from the Harmony Hub. As a test, I tried to kick off the node.js script to discover Harmony Hubs on a different computer, and I can confirm that the message being sent is what's being used in this code. However, it looks like the node.js project is actually instantiating it's own server (in the responseCollector.js server) which is receiving the response from the Harmony; it's not simply listening on port 61991, if that's possible. – ZbadhabitZ Feb 18 '16 at 22:19
  • The code in this answer is the same as the responseCollector.js - the only difference is that port 61991 is hardcoded as it is hard-coded in your original broadcast while the responseCollector.js uses a port that is specified in a variable. I confirmed via the nc command that the app does listen on that port. – Paulw11 Feb 18 '16 at 22:22
  • Hi @Paulw11 - Sorry, I wasn't clear. What I find interesting is that, if I send a message on port 61991 (either via the iOS app or using nc command), the node.js script doesn't pick up the message, though it does consistently pick up a response from the Harmony Hub each time. I've tried sending the message to a broadcast address, and directly to the IP of the computer running the node.js script, but I can't get it to pick it up as it does with the Harmony Hub response. Almost like the Harmony Hub response or the node.js script isn't just simply sending/receiving on 61991. – ZbadhabitZ Feb 19 '16 at 01:55
  • The node.js code doesn't use a fixed port 61991 like yours does; you need to know which port the node.js code sends in the message and is therefore listening on and then send to that port. – Paulw11 Feb 19 '16 at 01:57
  • I see. I've been running the "discover.js" file in the "examples" folder, which seems to be using port 61991 (that's where I got the port from in the first place). – ZbadhabitZ Feb 19 '16 at 02:11
  • I got it working. Editing my above question with the code used to resolve the issue. Your assistance was incredibly appreciated. – ZbadhabitZ Feb 19 '16 at 03:44