Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.


Socket programming in Swift: part 4 - SW design considerations

Updated on 2016-08-10 for Swift 3 Xcode 8 beta 3.

At the end of the previous post we had a socket that was listening for incoming connection requests. Before we start accepting these requests we need to spare a few moments on software design.

When we design a server, will there be multiple clients? Most of the time the answer will be yes. If that is the case, is performance an issue? Could the server profit from the parallel execution of client requests? In todays world multithreading is an issue that has to be considered. A server can optimize performance in two ways: either by using multiple cores or by doing something in between operations that cause the processor to wait. Like disk based operations, or communication with other computers, or even human intervention.

In the most simple example, a server will initialize a socket as in previous posts, do an accept of the first connection request, receive the data, return the result, and close all sockets. But this example would not be very useful imo. A more realistic example would do the initialization as in the previous posts, then enter a loop in which connection requests are accepted and then start a new thread for each connection request.

Moreover: A server will in general be started upon startup of the computer and not have a GUI. The configuration it needs can be read from a file, or a separate console application may be available to start/stop/configure & monitor the server software. When a console application is used, the server application must run a C&C loop (Command & Control loop) on a separate socket at the same time it is accepting connection requests from clients on the client port.

Therefore multithreading is a must for servers. A general approach would be this:

The Accept thread can be stopped by the C&C thread through the setting of a global variable. The Accept thread runs as long as it is not commanded to stop. While it runs it accepts connection requests and for each request starts a new Request thread. Requests threads are one-of threads. Once started they run to finish and terminate themselves. If Request threads can take a very long time to finish, it might be worth while to have a global variable that signals their termination as well. Of course the Request thread should monitor the global variable to see if it should terminate itself.

Once we start using multithreading, it is important to consider the priorities of each thread. The C&C Thread is not all that important, it can run at the normal priority level. The accept thread however should run at a higher priority such that connection requests are pulled form the accept queue as quickly as possible. The priority of the Request threads depends on your application, generally speaking I would put them below the C&C thread priority.

Since no post should be without code ;-), here is how I would code the setup of the socket and the start of the Accept thread:

(PS: Note that this is mockup code, not runnable in Playground without further work)

// =================================================
// Initialize the port on which we will be listening
// =================================================

let httpSocketDescriptor = initServerSocket(
    servicePortNumber: ap_HttpServicePortNumber,
    maxNumberOfConnectionsBeforeAccept: ap_MaxNumberOfHttpConnectionsWaitingToBeAccepted)

if httpSocketDescriptor == nil { applicationExit() } // Log entries should have been made

// ===========================================================================
// Keep on accepting connection requests until a fatal error or a stop request
// ===========================================================================

stopAcceptThread = false

let acceptQueue: DispatchQueue = DispatchQueue(
    label: "Accept queue",
    attributes: [.serial, .qosUserInteractive])

acceptQueue.async() { acceptConnectionRequests(httpSocketDescriptor!) }

The httpSocketDescriptor is an optional that has a value if the setup of the server socket (up to the listen call) was successful. The acceptConnectionRequests function needs this descriptor for the accept call. The acceptConnectionRequests will be the subject of the next post.

Next post: The accept call & starting a new thread.

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.

1 comment: