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

2016-02-17

Socket Programming in Swift: Part 8 - Server side considerations

After part 7 detailing the client side of things, what about the server side? What kind of connections does a server see, and how does it respond?

Unfortunately there are many more possibilities than can be described in a single post, so we will restrict ourselves to the following examples:
  1. Setup - Accept - (Receive - Close) - Close
  2. Setup - Accept - ([Receive] - Close) - Close
  3. Setup - Accept - (Receive - Close) - Accept - (Receive - Close) - Close
  4. Setup - Accept - [([Receive] - Close)] - Close
  5. Setup - Accept - [([Receive - [Transfer]] - Close)] - Close

The brackets indicate that the sequence is executed on a different socket than the Setup - Accept () - Close sequence outside the brackets.

The square brackets indicate that the sequence executed multiple times.

To reduce the number of possible scenario's only one receive-transmit scenario is shown. It is probably easy to see how "receive" can be replaced by "receive - transfer" for the other scenario's. Btw: It is of course also possible to skip the receive part entirely and only transfer data to clients.

When I talk about close here, I am assuming a close by the server. If a client closes a connection in between the normal sequence this is considered an error-scenario and is not covered here.

Common:

Before describing the scenario's, it is important to know that when an "accept" call is called on a (properly configured) server socket, it will wait until a client connects. When that happens, the accept call automatically creates a new socket (the client socket) on which all subsequent transfers to/from the connected client happens. Thus after a successful client connection a server has TWO open sockets. One on which it can accept further connections and one for the connected client.

If the server software can handle multiple client connections in parallel, then a second connection request will result in two open sockets for the two connections (assuming the first is not closed yet) and one open socket to accept further connection requests. For a total of THREE open sockets.

1 Setup - Accept - (Receive - Close) - Close


In this scenario the client transfers one data package to the server, and the server accept only one data package before terminating. This is probably not often used, but it can be used if a server has to shut down.
It can also be used to enhance security by timing the window in which a server accepts transfers.

Its advantage is simplicity; the server can be implemented in a single thread.

2 Setup - Accept - ([Receive] - Close) - Close


In this scenario the server accepts only 1 client, but that client can transfer multiple data packages. Once the client is done, it closes the connection. The server closed both its sockets.

In order to differentiate between data packages the client and server must agree on a mechanism to know when one package ends and another starts.

Though slightly more complex than no1, it still can be implemented in a single thread.

3 Setup - Accept - (Receive - Close) - Accept - (Receive - Close) - Close

In this scenario the server accepts two clients and both clients can transfer one data package to the server. However the clients cannot be connected to the server at the same time.
This kind of connection is useful in situations where there is no big harm in a delay before the server accepts a client connection.

Like no1 and no2 before, the server software can still be run in a single thread.

4 Setup - [Accept - ([Receive] - Close)] - Close

With this scenario we leave the single thread applications behind us. Multiple clients can connect -in parallel- to the server. And each client can send multiple data packages.

Not only must the server and client agree on a mechanism to differentiate between data packages, but the server software must also be multithreaded.
After each call to "Accept" the created client socket must be handled in its own thread.

Since there are multiple threads running at the same time, each with its own socket descriptor there is a danger that the server runs out of resources. Either in time or in memory. It is therefor important to manage the number of connections and only accept new connections if the resources are available. Among other this will mean that a time-out must be implemented on open connections.

5 Setup - [Accept - ([Receive - [Transfer]] - Close)] - Close

We finish of these examples with something that I think resembles the HTTP/2 protocol. It is fairly complex, and can profit from a multi-threading implementation even for a single client.

In this example a client connects to the server and sends some data. The server responds by sending multiple data packages back to the client. The client can then send more data to which is again replied by multiple data packages by the server.
In addition, this process can be handled by the server for multiple clients at the same time.

One of the ways this could be implemented server side is as follows:
1 - Setup of the server socket
2 - Accept a client connection
3 - Spin of the receiving of the client data to a new thread.
4 - In that thread receive the data and process the request in another new thread. If data must be returned then start a "data transfer thread" which reads the data to be transferred from a queue. The data receive thread would then fill the queue with data packages as it progresses. Once the received data is completely processed, terminate its thread and terminate the associated transfer thread.
5 - For each new data package received from the client start a new thread, as in point 4.
6 - Keep the connection to the client open until no-activity timeout or until the client closes.

As I said, this is fairly complex and needs to be thought through carefully. Don't simple assume that the above is correct!

Like with no4, the server must manage its resources carefully.

Part 9 is out: SwifterSockets

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!

Enjoy.

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