Updated on 2016-08-11 for Swift 3 Xcode 8 beta 3 & use in playground.
In this part I'll show the code I used to accept incoming connection requests and spin off the request into a queue to be processed in another thread:
(PS: You could place this code after the listening call previously in this series, but it really should reside in its own loop as has been discussed.)
...{...
// Incoming connections will be executed in this queue (in parallel)
let connectionQueue = DispatchQueue(
label: "ConnectionQueue",
attributes: [.concurrent, .qosUserInteractive]
)
// ========================
// Start the "endless" loop
// ========================
ACCEPT_LOOP: while true {
// =======================================
// Wait for an incoming connection request
// =======================================
var connectedAddrInfo = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
var connectedAddrInfoLength = socklen_t(sizeof(sockaddr.self))
let requestDescriptor = accept(socketDescriptor, &connectedAddrInfo, &connectedAddrInfoLength)
if requestDescriptor == -1 {
let strerr = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Accept error \(errno) " + strerr
print(message)
// #FEATURE# Add code to cop out if errors occur continuously
continue
}
let (ipAddress, servicePort) = sockaddrDescription(addr: &connectedAddrInfo)
let message = "Accepted connection from: " + (ipAddress ?? "nil") + ", from port:" + (servicePort ?? "nil")
print(message)
// ==========================================================================
// Request processing of the connection request in a different dispatch queue
// ==========================================================================
connectionQueue.async() { receiveAndDispatch(socket: requestDescriptor)}
}
}
func receiveAndDispatch(socket: Int32) {
}
This is simple enough, the ACCEPT_LOOP waits for incoming requests, and when it accepts a request it returns a new socket descriptor. In the code above you can see that it waits to accept connections on the socketDescriptor, but then processes the requests with the requestDescriptor. Under the hood the OS has created a new socket descriptor to handle the incoming connection. Since the incoming request now has its own socket descriptor the application (and OS) can continue to listen/accept on the original socket descriptor for more incoming connection requests.The accepted request is further processed in the receiveAndDispatch function which is executed asynchronously (i.e. in a parallel thread) on the connectionQueue As we shall see in the next post, this function is responsible for handling all data transfers of the connection and to close that connection when done.
The sockaddrDescription function was included in part 1 of this series.
Note: The above ACCEPT_LOOP never terminates. If a Command and Control loop is implemented in a different thread then the loop should probably test a global variable to see if it should continue accepting requests or not.
Note 2: As indicated in the code, if every accept call returns an error, the ACCEPT_LOOP should be broken as well. This code is not included in this example.
Note 3: Accept will wait for a new connection request, until then the thread the accept loop is in will be blocked. If that is not acceptable, then use the select() function to see if there is anything to accept.
Next is part 6: Select and recv
Check out my Port Spy app in the App Store. A utility that helps you debug your socket based application and includes its own source code. So you can see first hand how to implement socket based io in Swift. And you will be helping this blog!
Happy coding...
Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH
We don't get the world we wish for... we get the world we pay for.
No comments:
Post a Comment