After setting the socket options (in the previous post) we now have to actually connect the socket (descriptor) to a port on your computer. This is done by the bind call. The following code does this and follows on the code from the previous post.
let maxNumberOfConnectionsBeforeAccept: Int32 = 20
// ====================================
// Bind the socket descriptor to a port
// ====================================
status = bind(
socketDescriptor, // The socket descriptor of the socket to bind
servinfo!.pointee.ai_addr, // Use the servinfo created earlier, this makes it IPv4/IPv6 independant
servinfo!.pointee.ai_addrlen) // Use the servinfo created earlier, this makes it IPv4/IPv6 independant
print("Status from binding: \(status)")
// Cop out if there is an error
if status != 0 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Binding error \(errno) (\(strError))"
freeaddrinfo(servinfo)
close(socketDescriptor) // Ignore possible errors
print (message)
return
}
// ===============================
// Don't need the servinfo anymore
// ===============================
freeaddrinfo(servinfo)
// ========================================
// Start listening for incoming connections
// ========================================
status = listen(
socketDescriptor, // The socket on which to listen
maxNumberOfConnectionsBeforeAccept) // The number of connections that will be allowed before they are accepted
print("Status from listen: " + status.description)
// Cop out if there are any errors
if status != 0 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Listen error \(errno) (\(strError))"
print(message)
close(socketDescriptor) // Ignore possible errors
return
}
After the bind call, the servinfo structure is no longer needed, thus it is deallocated. This must be done explicitly as it was allocated by a low level operation that bypasses the ARC.
This was all the preparation that is needed to set up a port for listening. All that remains is to start the actual listening for incoming connections. Not surprisingly this is done with the listen call. This call takes two arguments, the first is the socket descriptor, the second is a number that indicates how many connections can be kept waiting before the are accepted. (The accept call is shown in one of the following posts). If this number is, say 5, then 5 clients can have a pending connection to our server before our server 'accepts' the incoming request. Connection requests are -more or less- accepted in sequence of arrival, hence the second parameter can be seen as the size of the backlog queue.
What number should you choose? Well, that depends on the performance of your server as compared to the peak load in requests. You don't want clients to wait too long, but then again, dropped connection requests are also not perfect. Your choice.
Lets look at an example: Say your server can handle 10 requests simultaneously, and takes on average 100 milli seconds per request. Thus the 11-th request will remain in the accept (backlog) queue for (on average) 1 second. A backlog queue with 20 entries means that the last connection request to enter the queue could have to wait (on average) 3 seconds before it is processed. The 21-th request would be refused, and hence the client would see its request dropped.
(In general, it would possibly be better to have a process that handles overload situations differently. For example by implementing an algorithm that detects overloads, en then replies to a new request with a very fast response telling the client to try again in a few seconds or minutes.)
Part 4: SW Design consideration (multithreading)
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.
Has anyone tried this in Swift 4?
ReplyDeleteI always get the same result:
HostIp: :: at port: 3333
HostIp: 0.0.0.0 at port: 3333
Not yet, I am still on XCode 8 due to older HW. :-(
ReplyDelete