The behavior of UDP communication changes depending on the status of the iOS device

I am making an application for UDP communication.

 - use CocoaAsyncSocket

- use multicast


However, the behavior changes in the following environment.


If connect to your Mac with a Lightning cable while running the iOS app (UDP transmission / reception is working without problems), the transmission / reception during operation will not work.


-For iPhone (cellular),

 Mobile data communication ON : Do not send / receive

 Mobile data communication OFF: Send and receive


When connected to a Mac, transmission / reception does not work, so real-time debugging is not possible.


When performing UDP communication (multicast)

Are there any settings that need to be made on a Mac or iOS device?


Development environment:

Xcode 11.3

MacOS 10.15.2 (Catalina)

iPhone Cellular iOS13.3

iPad Pro Wifi iOS13.3

 


- iPhone / iPad access point is not connected to internet.

- Mac access point is connected to internet.

Accepted Reply

1. Is it possible to prevent the active interface from switching?

No. iOS’s interfaces are controlled by a combination of external events (for example, entering and leaving signal range) and user activities (for example, turning off Wi-Fi in Settings).

FYI, there really isn’t such a thing as “the active interface”. Rather, there can be multiple active interfaces. I suspect you’re actually referring to the interface associated with the default route, but even that’s complex in the presence of full tunnel VPN.

2. Does getting the interface use Network framework?

Network framework has infrastructure for dealing with interfaces, namely,

NWInterface
and all the construct that use that. If you have an
NWInterface
, you can bridge over to BSD using its
name
and
index
properties. If you want to find the Wi-Fi interface, the best way to do that is using
NWPathMonitor
, instantiating it with
NWInterface.InterfaceType.wifi
.

Pasted in below is the code for a small command-line tool that prints the current list of Wi-Fi interface, updating as interfaces are attached and detached.

Share and Enjoy

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

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

func main() -> Never {
    let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
    monitor.pathUpdateHandler = { path in
        print(path.availableInterfaces.map { $0.name })
    }
    monitor.start(queue: .main)
    dispatchMain()
}

main()

Replies

The behavior of UDP [multicast] communication changes depending on the status of the iOS device

Yes it does. UDP multicast is tricky to get right, and such code generally has to be interface aware, that is, track the current active interfaces and adjust as interfaces come and go.

Reading through the rest of your post you provided a lot of detail about the behaviour you’re seeing but very little detail on the behaviour you want. Can you elaborate on that? What are you trying to do with UDP multicast?

Share and Enjoy

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

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

Thank you for your response.

My knowledge about UDP-multicast was not enough.

Thank you for telling me.



What I want to do is:

- With an iOS device, Send and receive data with custom sensors.

- Operating environment,

- Internet disconnected router (a)

- Connect multiple iOS devices and sensors to a


Let me ask you additional questions.

1. When the Mac and iOS device are connected, the interface is not switched. Can it be done with the setting app?

2. Does getting the interface use Network.framework? Would you please let me know if there is a useful site?

So, let me see if I understand this properly:

  • You have a Wi-Fi access point.

  • It is not able to route traffic to the wider Internet.

  • Various sensors are joined to its Wi-Fi network.

  • The iOS device also joins its Wi-Fi network.

  • You want to be able to send multicast UDP requests from the iOS device to that Wi-Fi network.

  • You also want to receive responses from those sensors.

Is that correct?

Does a sensor multicast its response? Or unicast it?

Share and Enjoy

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

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

I'm sorry.

In some cases, the explanation has been simplified.



>> Various sensors are joined to its Wi-Fi network.

I mentioned that multiple sensors will connect to Wifi,

In fact, there is a converter that bundles the sensor's wireless communication, and that converter is connected to Wifi.

The converter is performing multicast processing.

Also, different ports are used for "iOS app"→"Converter" communication and "Converter"→"iOS app" communication.


Items other than the above are correct.

Thanks for the clarifications.

One a few more questions:

  • Is this all IPv4? Or is IPv6 involved?

  • With regards ports, are these ports fixed? Or dynamic?

Share and Enjoy

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

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

>Is this all IPv4? Or is IPv6 involved?


IPv4 was assumed as the communication specification.

I want to support IPv6.


Does the answer fit the intent of the question?




>With regards ports, are these ports fixed? Or dynamic?

The port number for communication is fixed for both transmission and reception.

First up, you should be able to receive the unicast replies from your accessory using any UDP API. Just make sure to bind to the ‘any’ address (

INADDR_ANY
in BSD Sockets).

In constrast, sending the multicasts is a lot less straightforward:

  • You will need to use BSD Sockets because Network framework does not yet support multicasts.

  • You should create a socket for each interface that you want to send on.

  • You should bind that socket using the specific interface you want to use. I generally use

    IP_BOUND_IF
    for this, but
    IP_MULTICAST_IF
    should also work.
  • Beyond that, have a read of the Multicast Options section of the ip man page. Most of it focuses on receiving multicasts, which is the really tricky problem, but there’s some good tidbits about sending as well.

Share and Enjoy

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

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

Thanks for the information about the implementation.


Again, let me ask you a new question, including the one you posted earlier.


1. Is it possible to prevent the active interface from switching?

2. Does getting the interface use Network.framework? *I know C APIs getifaddrs(struct ifaddrs **)

1. Is it possible to prevent the active interface from switching?

No. iOS’s interfaces are controlled by a combination of external events (for example, entering and leaving signal range) and user activities (for example, turning off Wi-Fi in Settings).

FYI, there really isn’t such a thing as “the active interface”. Rather, there can be multiple active interfaces. I suspect you’re actually referring to the interface associated with the default route, but even that’s complex in the presence of full tunnel VPN.

2. Does getting the interface use Network framework?

Network framework has infrastructure for dealing with interfaces, namely,

NWInterface
and all the construct that use that. If you have an
NWInterface
, you can bridge over to BSD using its
name
and
index
properties. If you want to find the Wi-Fi interface, the best way to do that is using
NWPathMonitor
, instantiating it with
NWInterface.InterfaceType.wifi
.

Pasted in below is the code for a small command-line tool that prints the current list of Wi-Fi interface, updating as interfaces are attached and detached.

Share and Enjoy

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

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

func main() -> Never {
    let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
    monitor.pathUpdateHandler = { path in
        print(path.availableInterfaces.map { $0.name })
    }
    monitor.start(queue: .main)
    dispatchMain()
}

main()

I will try the method you taught.


Thank you very much.