It’s broadcast.
Blat!
We do not have a good story for UDP broadcasts in Network framework (r. 103252571), which means you have to use BSD Sockets )-:
Calling the BSD Sockets API from Swift is, as I mentioned before, un-fun. Over the years I’ve come up with many different approaches for this — you can see one of my early attempts here — but these days I tend to lean in to getaddrinfo
and getnameinfo
. For example, using the QSocketHelpers
functions below, it’s easy to wrap routines that need a sockaddr
pointer, like bind
:
import System
extension FileDescriptor {
func bind(address: String, port: UInt16) throws {
try QSocketHelpers.withSockAddr(address: address, port: port) { sa, saLen in
let success = Darwin.bind(self.rawValue, sa, saLen) >= 0
guard success else {
let err = errno
throw Errno(rawValue: err)
}
}
}
}
and routines that return an address via a sockaddr
pointer, like getsockname
:
extension FileDescriptor {
func getsockname() throws -> (address: String, port: UInt16) {
let (_, address, port) = try QSocketHelpers.fromSockAddr { sa, saLen in
let success = Darwin.getsockname(self.rawValue, sa, &saLen) >= 0
guard success else {
let err = errno
throw Errno(rawValue: err)
}
}
return (address, port)
}
}
This means representing addresses as strings, which is not ideal. I wouldn’t do this if I were designing a networking library from scratch but I’ve found that this approach represents a good balance between convenience and flexibility when messing around with BSD Sockets.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
enum QSocketHelpers {
static func withSockAddr<ReturnType>(
address: String,
port: UInt16,
_ body: (_ sa: UnsafePointer<sockaddr>, _ saLen: socklen_t) throws -> ReturnType
) throws -> ReturnType {
var addrList: UnsafeMutablePointer<addrinfo>? = nil
var hints = addrinfo()
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV
let err = getaddrinfo(address, "\(port)", &hints, &addrList)
guard err == 0 else { throw QSocketHelpers.NetDBError(code: err) }
guard let addr = addrList else { throw QSocketHelpers.NetDBError(code: EAI_NODATA) }
defer { freeaddrinfo(addrList) }
return try body(addr.pointee.ai_addr, addr.pointee.ai_addrlen)
}
static func fromSockAddr<ReturnType>(_ body: (_ sa: UnsafeMutablePointer<sockaddr>, _ saLen: inout socklen_t) throws -> ReturnType) rethrows -> (result: ReturnType, address: String, port: UInt16) {
var ss = sockaddr_storage()
var saLen = socklen_t(MemoryLayout<sockaddr_storage>.size)
return try withUnsafeMutablePointer(to: &ss) {
try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { sa in
let result = try body(sa, &saLen)
var host = [CChar](repeating: 0, count: Int(NI_MAXHOST))
var serv = [CChar](repeating: 0, count: Int(NI_MAXSERV))
let err = getnameinfo(sa, saLen, &host, socklen_t(host.count), &serv, socklen_t(serv.count), NI_NUMERICHOST | NI_NUMERICSERV)
guard err == 0 else { throw QSocketHelpers.NetDBError(code: err) }
guard let port = UInt16(String(cString: serv)) else { throw QSocketHelpers.NetDBError(code: EAI_SERVICE) }
return (result, String(cString: host), port)
}
}
}
struct NetDBError: Error {
var code: CInt
}
}