QSocket: I/O

This thread has been locked by a moderator.

IMPORTANT If you haven’t yet read Calling BSD Sockets from Swift, do that first.

To read and write a connected socket, use the FileDescriptor read and write methods.

To read and write an unconnected socket, you need helpers to convert the address:

extension FileDescriptor {

    /// Sends a datagram to an address.
    ///
    /// Equivalent to the `sendto` BSD Sockets call.
    ///
    /// If you’re working with a TCP socket, use
    /// ``write(data:retryOnInterrupt:)`` method.
    ///
    /// - important: This builds the destination address from the supplied
    /// string every time you send a datagram.  That’s horribly inefficient.
    /// That’s not a problem given the design constraints of this package but,
    /// oh gosh, don’t use this in a real project.
    ///
    /// If the socket is non-blocking, be prepare for this to throw `EAGAIN`.
    ///
    /// The result is discardable because this method is most commonly used with
    /// a UDP socket and that’s all or nothing.

    @discardableResult
    func send(data: Data, flags: CInt = 0, to destination: (address: String, port: UInt16), retryOnInterrupt: Bool = true) throws -> Int {
        try data.withUnsafeBytes { buf in
            try QSockAddr.withSockAddr(address: destination.address, port: destination.port) { sa, saLen in
                try errnoQ(retryOnInterrupt: retryOnInterrupt) {
                    // If `count` is 0 then `baseAddress` might be zero.  We’re
                    // assuming that the `sendto` call will be OK with that.
                    Foundation.sendto(self.rawValue, buf.baseAddress, buf.count, flags, sa, saLen)
                }
            }
        }
    }

    /// Receive a datagram and its source address.
    ///
    /// Equivalent to the `recvfrom` BSD Sockets call.
    ///
    /// If you’re working with a TCP socket, use the
    /// ``read(maxCount:retryOnInterrupt:)`` method.
    ///
    /// - important: This builds the destination address string from the
    /// returned address every time you receive a datagram.  That’s horribly
    /// inefficient. That’s not a problem given the design constraints of this
    /// package but, oh gosh, don’t use this in a real project.
    ///
    /// If the socket is non-blocking, be prepare for this to throw `EAGAIN`.
    ///
    /// The result is non-optional because UDP allows us to send and receive
    /// zero length datagrams.

    func receiveFrom(maxCount: Int = 65536, flags: CInt = 0, retryOnInterrupt: Bool = true) throws -> (data: Data, from: (address: String, port: UInt16)) {
        var result = Data(count: maxCount)
        let (bytesRead, address, port) = try result.withUnsafeMutableBytes { buf in
            try QSockAddr.fromSockAddr { sa, saLen in
                try errnoQ(retryOnInterrupt: retryOnInterrupt) {
                    recvfrom(self.rawValue, buf.baseAddress, buf.count, flags, sa, &saLen)
                }
            }
        }
        result = result.prefix(bytesRead)
        return (result, (address, port))
    }
}

Wrappers for the other BSD Sockets I/O primitives are left as an exercise for the reader [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Good luck with sendmsg and recvmsg! (-:

Up vote post of eskimo
269 views