Paulo Andrade

Keeper of Secrets

twitter github stackoverflow linkedin email
Quick TCP Server Using NSSocketPort
Oct 22, 2018
2 minutes read

For several occasions now, I’ve found myself in the need to spin up a simple TCP server when writing tests. Usually my needs are very simple, I just need to accept some connection and stub some responses.

There are plenty of libraries out there that serve this purpose but it feels such an overkill to add another dependency just for running my tests. So I usually just end up calling socket, bind and listen even though that’s still a lot of code for something so simple and feels out of place in Cocoa.

More recently I discovered NSSocketPort and it sure looked like it could replace most of that low level socket code, so I took it for a spin:

let serverSocket = SocketPort()
let serverHandle = FileHandle(fileDescriptor: serverSocket.socket, closeOnDealloc: true)

NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
                                       object: serverHandle,
                                       queue: OperationQueue.main) { (note) in
    guard let acceptedFileHandle = note.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else {
        return
    }
    // read and write to acceptedFileHandle
    // to communicate with your client
    
}

serverHandle.acceptConnectionInBackgroundAndNotify()

With the example code above you’re now ready to accept and respond to TCP connections. And you get to do that using NSFileHandle where you can read and write NSData directly and install a readabilityHandler to be notified when data arrives.

Note no port was specified, this was on purpose, I wanted the OS to automatically choose any available port. To find out which port was chosen you have to look at the NSSocketPort’s address a delve down to C for a bit.

var serverPort: UInt16 = 0
serverSocket.address.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
    var addr = sockaddr_in()
    memcpy(&addr, bytes, MemoryLayout<sockaddr_in>.size)
    serverPort = addr.sin_port.bigEndian
}

And voilá. Next time you need to do something similar perhaps you’ll think twice before jumping on GitHub 🙃.



Back to posts