Unable to read UDP Multicast packets

We are having trouble receiving UDP multicast packets sent by a server every half a second. We are receiving with an IOS app, connected to the server directly via wifi, and we are using the CocoaAsyncSocket library to join the multicast group. Multicast packets are sent to destination port 12347 and the multicast group address is 225.0.0.37. We are certain that:


a) The rest of the app logic does not interfere to create the problem, i.e. an ios app with just the multicast logic connecting to the server is still problematic.


b) Other swift networking libraries with similar functionality did not solve the issue.


c) Other users have tried the exact same code we are using for their multicast connections successfully.


d) The UDP packets multicast by the server can be received by other apps. We have tried the android version of the same app which can successfully receive the packets, and we have used Wireshark (network packet sniffer) which also senses the UDP packets. We have not tried to receive the packets with a similar IOS app that would work in these circumstances because we couldn't find such an app.


e) We have noticed one specific sequence of steps we can take to successfully receive packets. That is, if:


1) We are connected to a different network upon launching the app.

2) We then navigate to the screen that opens the multicast socket.

3) Without closing the app (just pressing the home button) go to the wifi settings and switch to the server network.

4) Navigate back to the opened app.


This leads us to believe that for some reason we can only receive multicast UDP packets if the server wifi network is joined after the socket is opened.



Here is the code used for the UDP socket:

    override func viewDidLoad() {
        super.viewDidLoad()

        let socket = GCDAsyncUdpSocket(delegate: self, delegateQueue:DispatchQueue.main)
        do {try socket.bind(toPort: 12347)} catch {print(error.localizedDescription)}
        do {try socket.enableBroadcast(true)} catch {print(error.localizedDescription)}
        do {try socket.joinMulticastGroup("225.0.0.37")} catch { print(error.localizedDescription) }
        do {try socket.beginReceiving()} catch {print(error.localizedDescription)}
    }

    func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
          //this does not get called
          print(data.description)
    }

It is worth noting that we have tried both with and without the enableBroadcast command, it did not make any difference. Also, none of the catch statements are hit.


Here is the packet information captured by wireshark:

https://imagizer.imageshack.com/img924/7397/fAXebI.png


Any idea on what might be causing this?

Two things:

  • Use an RVI packet trace to confirm that the iOS device actually received the multicast. See Recording a Packet Trace for more information about RVI.

  • If you’re switching apps as part of this process, you’re probably suffering from socket resource reclaim. See Technote 2277 Networking and Multitasking for more background on that. How you address this depends on your specific circumstances, but there’s basically two approaches:

    • Prevent your app from suspending (A)

    • Shut down your networking when you move to the background and restore it when you move to the foreground (B)

You can do A, at least for short periods of time, using a UIApplication background task.

Note In B, technically what matters is whether you get suspended, so read become eligible for suspension and are no longer eligible for suspension in place of move to the background and move to the foreground.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

In regards to your second point, we are not switching apps at any point. In my question I did mention navigating away from the app in order to connect to the server network but that is part of a solution we identified for the issue. That solution is, however, not practical for users of the app, hence the question.


The first point was helpful, we managed to get some additional information about the problem. We applied RVI using both a general filter for all UDP packets and a more specific filter for all udp packets on port 12347, using the following commands:


sudo tcpdump -i rvi0 udp -vv
 
sudo tcpdump -i rvi0 udp port 12347 -vv


This is a sample output when we receive a packet:


12:59:05.489716 IP (tos 0xb,CE, ttl 1, id 11640, offset 0, flags [DF], proto UDP (17), length 60)

192.168.1.1.56827 > 225.0.0.37.12347: [udp sum ok] UDP, length 32


It appears that with either filter, the packets do not appear in the tcp dump unless we apply the solution previously described in my question, i.e. be connected to a different network (A), navigate to the view controller that initialises the port, switch the network to the server network (B) while the app is open in the background, and navigate back to the app. In fact, we noticed that we don't need to navigate back to the app, the packets start showing up in the tcp dump the moment we switch to the server network (B) from the settings screen.


To give you some additional information, the network we are initialy connected to (A) has internet connection, whereas the server network (B) has no internet connection. We have tried the same solution replacing the initial network (A) with 2 alternatives:


1) No Initial network, i.e. we are connected to no network before connecting to (B)

2) Another server network (which also has no internet connection)


We have found that in both of these cases, we get no packets coming through. This leads us to believe that the issue might have something to do with internet connectivity, i.e we have to be connected to the internet when opening the socket in order to receive the packets for some reason.


Any help regarding the issue would be appreciated.

In line 5 of the code snippet in your original post, what does

socket.bind(toPort: 12347)
end up passing to the
bind
system call?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The bind system call is executed two times, for an IPv4 and IPv6 address. The bind system call expects 3 arguments : an unnamed int, sockaddr and socklen_t. These are their values:


IPv4 call


unnamed int : 3

sockaddr : 10 02 30 3B 00 00 00 00 00 00 00 00 00 00 00 00

socklen_t : 16


IPv6 call


unnamed int : 4

sockaddr : 1C 1E 30 3B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

socklen_t : 28


In both cases the returning status is 0. The arguments do not change if the network changes.

OK, that’s what I was hoping for. Specifically, in the IPv4 case, you’re passing

INADDR_ANY
to the
sin_addr
field.

Unfortunately that means I’m out of ideas as to what’s causing your multicast reception to fail. I have one final suggestion, and if that doesn’t work out then I’m going to recommend that you open a DTS tech support incident so that I can take a good look at your code.

My final suggestion is that you do what I always do when I’m trying to work out multicast problems: Look at the

mDNSResponder
source code (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Did you ever figure this out? I'm having trouble with a similar set up.

Unable to read UDP Multicast packets
 
 
Q