How to work with UDP sockets in iOS, swift?
This solution work for me! Thanks @Paulw11
Swift 4, XCode 10.1, iOS 12.0
Simple connect to the public UDP server (This is NOT optimal version but works):
import UIKit
import Network
class ViewController: UIViewController {
var connection: NWConnection?
var hostUDP: NWEndpoint.Host = "iperf.volia.net"
var portUDP: NWEndpoint.Port = 5201
override func viewDidLoad() {
super.viewDidLoad()
// Hack to wait until everything is set up
var x = 0
while(x<1000000000) {
x+=1
}
connectToUDP(hostUDP,portUDP)
}
func connectToUDP(_ hostUDP: NWEndpoint.Host, _ portUDP: NWEndpoint.Port) {
// Transmited message:
let messageToUDP = "Test message"
self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
self.connection?.stateUpdateHandler = { (newState) in
print("This is stateUpdateHandler:")
switch (newState) {
case .ready:
print("State: Ready\n")
self.sendUDP(messageToUDP)
self.receiveUDP()
case .setup:
print("State: Setup\n")
case .cancelled:
print("State: Cancelled\n")
case .preparing:
print("State: Preparing\n")
default:
print("ERROR! State not defined!\n")
}
}
self.connection?.start(queue: .global())
}
func sendUDP(_ content: Data) {
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
func sendUDP(_ content: String) {
let contentToSendUDP = content.data(using: String.Encoding.utf8)
self.connection?.send(content: contentToSendUDP, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
func receiveUDP() {
self.connection?.receiveMessage { (data, context, isComplete, error) in
if (isComplete) {
print("Receive is complete")
if (data != nil) {
let backToString = String(decoding: data!, as: UTF8.self)
print("Received message: \(backToString)")
} else {
print("Data == nil")
}
}
}
}
}
Swift UDP Connection Issue
In your function receive
you are using the NWConnection.receiveMessage
if you check the documentation here:
https://developer.apple.com/documentation/network/nwconnection/3020638-receivemessage
You'll see that it schedules a single receive completion handler. That means that you'll have to do something to trigger it again. What I normally do is have a function like:
private func setupReceive() {
connection.receive(minimumIncompleteLength: 1, maximumLength: MTU) { (data, _, isComplete, error) in
if let data = data, !data.isEmpty {
let message = String(data: data, encoding: .utf8)
print("connection \(self.id) did receive, data: \(data as NSData) string: \(message ?? "-")")
self.send(data: data)
}
if isComplete {
self.connectionDidEnd()
} else if let error = error {
self.connectionDidFail(error: error)
} else {
self.setupReceive() // HERE I SET THE RECEIVE AGAIN
}
}
}
That way, after processing the read, you end up setting up another single receive completion handler.
If you want to see a full example, you can check my article on using Network.framework
here:
https://rderik.com/blog/building-a-server-client-aplication-using-apple-s-network-framework/
The example uses TCP instead of UDP, but it should give a general idea.
UDP Listener on iOS 14
I was able to explore this some more and got some help via the apple developer forums, posting an answer here as well for those who are interested.
I ended up using an NWListener to listen for UDP packets, then set up an NWConnection once once I'd received something. I use this NWConnection to read data from the UDP broadcast.
From Quinn "The Eskimo:"
Listening for UDP broadcasts via an
NWListener
and then using theNWConnection
objects it vends (via the new connection handler) to communicate over unicast with the broadcast’s sender is an expected use case.
I encourage anyone reading this to check out our discussion on the Apple Developer Forum as well.
Here is my implementation:
var udpListener: NWListener?
var udpConnection: NWConnection?
var backgroundQueueUdpListener = DispatchQueue.main
func findUDP() {
let params = NWParameters.udp
udpListener = try? NWListener(using: params, on: 15000)
udpListener?.service = NWListener.Service.init(type: "_appname._udp")
self.udpListener?.stateUpdateHandler = { update in
print("update")
print(update)
switch update {
case .failed:
print("failed")
default:
print("default update")
}
}
self.udpListener?.newConnectionHandler = { connection in
print("connection")
print(connection)
self.createConnection(connection: connection)
self.udpListener?.cancel()
}
udpListener?.start(queue: self.backgroundQueueUdpListener)
}
func createConnection(connection: NWConnection) {
self.udpConnection = connection
self.udpConnection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("ready")
self.send()
self.receive()
case .setup:
print("setup")
case .cancelled:
print("cancelled")
case .preparing:
print("Preparing")
default:
print("waiting or failed")
}
}
self.udpConnection?.start(queue: .global())
}
func endConnection() {
self.udpConnection?.cancel()
}
Swift and UDP socket packet send twice
For everyone is interested, this issue is caused by the network. At home I've a Vodafone network, with their router (Vodafone Station Revolution). The Vodafone Station Revolution generate Wi-Fi @ 2,4GHz and Wi-Fi @ 5GHz. When I try to send an UDP packet on this network this packet will posted twice one for the 2,4GHz and one for the 5GHz network. So to solve my issue I disabled the 5GHz network and all work fine. It is not a code problem, indeed it's a network problem.
I hope my experience will be useful for other person who need the send UDP packet on Vodafone network.
By the way thank you for help.
UDP Connection failed - Error No route to host since the new iOS Update 14.5.1
I found out you have to request the Multicast Networking Entitlement from Apple:
https://developer.apple.com/contact/request/networking-multicast
Since iOS 14.5 you have to request to use UDP...
If they give you the permission to use it, just follow these steps explained here:
https://developer.apple.com/forums/thread/663271
They usually answer after 1-2 days.
Related Topics
Macos, Swift 3: How to Get Data Back After Segue
Prevent Nscollectionview 'Lifting' an Item During Drag
Same Object Different Address. Why
Place Multiple Scn Objects in Touchesbegan Method
Protocol Extension Doesn't Work with Rct_Export_View_Property
How to Use Mtlblitcommandencoder for Copying Interlaced Video Fields into a Mtlbuffer
Having Trouble with Musickit Sample App Provided by Apple
Calling Closure Inside Closure
Use The Same View for Adding and Editing Coredata Objects
How to Read The Property Values of a JSON Error Object Using Combine in Swift
Allow Siri Remote Menu Button When Play/Pause Button Is Overridden
Implement a Custom Staggeregrid in UIview Like Etsy App in Swift
Create Skspritenode with an Asset Programmatically
Swift: How to Continuously Send an Action from a Nstextfield
Thread Safety of Method Calls on "Shared" Static Constant Property
Cloudkit: How to Access Main User's Attributes
Metal: Limit Mtlrendercommandencoder Texture Loading to Only Part of Texture