The symptom is that no multicast packets are received.
There are lots of potential reasons for that, most of which are unrelated to the multicasts entitlement. Unfortunately it’s hard to offer concrete advice given that you’re using a third-party wrapper around (presumably) BSD Sockets.
Let me start by asking a simple question: What are you using multicasts for? I presume you’re building a game, and most game developers who hit this issue are implementing local network multiplayer. If that’s the case:
-
You can avoid this whole exercise by using GameKit.
-
If GameKit doesn’t work for you — perhaps you want to support non-Apple platforms — adopt Bonjour [1]. This is widely supported on non-Apple platforms (including Windows, Android, and Linux).
Both of these get you out of the business of dealing with multicasts directly. They’re also likely to be more efficient on the network than any custom solution you create.
If you’re must use multicasts directly, it’s possible that you can avoid this grief by adopting Network framework. Here’s a trivial example of how to set that up in Swift:
// Multicast addresses are those reserved for experimental use by RFC
// 4727.
//
// <https://tools.ietf.org/html/rfc4727>
let groupAddress = NWEndpoint.hostPort(host: "224.0.0.254", port: 12345)
let description = try NWMulticastGroup(for: [groupAddress])
let group = NWConnectionGroup(with: description, using: .udp)
group.setReceiveHandler(maximumMessageSize: 64 * 1024) { … }
group.stateUpdateHandler = { … }
group.start(queue: .main)
If you can’t use Network framework, you have to deal with BSD Sockets )-: In that case it’s best to target a specific interface explicitly. Without that, the system has to choose the interface to target, and my experience is that the system rarely does what you want [2].
Unfortunately that requires you to work with network interfaces directly, which is an exercise fraught with peril. I discuss this in much detail in Extra-ordinary Networking.
Here’s a snippet that shows how to set up a socket to send and receive multicasts on a specific interface:
// Multicast addresses are those reserved for experimental use by RFC
// 4727.
//
// <https://tools.ietf.org/html/rfc4727>
let groupAddress = (address: "224.0.0.254", port: 12345 as UInt16)
let socket = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)
// Join the group.
let ifAddr = interfaceAddress()
let groupAddrIP = in_addr(s_addr: inet_addr(groupAddress.address))
let ifAddrIP = in_addr(s_addr: inet_addr(ifAddr))
var req = ip_mreq(imr_multiaddr: groupAddrIP, imr_interface: ifAddrIP)
try socket.setSocketOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, MemoryLayout.size(ofValue: req))
// Bind the socket.
try socket.setSocketOption(SOL_SOCKET, SO_REUSEPORT, 1 as CInt)
try socket.bind("0.0.0.0", groupAddress.port)
// Enable non-blocking mode.
try socket.setNonBlocking()
This is using the QSocket wrapper discussed here.
The tricky part with this code is the interfaceAddress()
function. For advice on that, see Don’t Try to Get the Device’s IP Address. Specifically, if you are doing service discovery then follow the advice in the Service Discovery section of that post.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] An Apple term for three Internet standards:
[2] Worse yet, it might do the right thing in your environment but then fail in the field, or change behaviour over time.