A Multi-client Server Design Pattern Using Simple TCP/IP Messaging

Publish Date: May 14, 2011 | 31 Ratings | 3.61 out of 5 |  PDF

Overview

This document describes a server application design pattern that supports multiple client connections. It extends the command-based communication example to handle dynamic connections and disconnections.

Table of Contents

  1. Background
  2. Example Application
  3. Implementation Options
  4. Connection Manager
  5. Server Implementation Overview
  6. New Connection Monitor
  7. Command Parser
  8. Medium Priority Tasks
  9. Conclusion

1. Background

Client/server systems provide access to a central application from one or more remote clients. For example, a server application may perform some measurement or automation function (such as test cell control) and client applications may provide operators with a user interface for monitoring the state or progress of that function.

In multi-client applications, clients may connect and disconnect at random times. For example, during HIL batch tests that run for extended periods of time, various users may connect to the system several times a day to check on the progress and status of test that are of interest to them.

In order to support this scenario, the server software should be able to dynamically accept and service any number of incoming connections. The server should also keep track of client requests and be able to service each client in an individual way. For example, if the server acquires multiple channels of data, clients should be able to request a channel subset that is managed on a per-connection basis.

The goal of this document is to describe a server design pattern that can run indefinitely, continuously monitoring for new connections and servicing them accordingly.

An installer for the Simple TCP/IP Messaging (STM) component and multi-client server example code can be found at the link you will find at the end of this document.  If you are not familiar with the Simple TCP/IP Messaging protocol and the Command-based Communication, design pattern, we recommend that you read about them before continuing with this document.

This document discusses an approach to LabVIEW network communication using the TCP/IP protocol. You may also want to consider using Shared Variables, which provide a higher-level programming interface and abstract most of the implementation details described here.

Back to Top

2. Example Application

The example application used in this document has the following features: Server
- Simulates the acquisition of 4 data channels
- Accepts any number of clients
- Receives commands such as stop and desired channel from the client
- Sends data to each of the connected clients. Each client only receives the data they requested

Client (each client is identical)
- Connects, disconnects and reconnects to the server
- Sends commands such as stop and desired channel
- Receives data from the server and displays it on a graph


Figure 1. Multi-client Server Overview


Back to Top

3. Implementation Options

When implementing a server capable of handling multiple connections, there are a couple of approaches you can take.

One approach uses singleton servers for each connection. Each time a new connection is established, the server launches a new instance of the singleton routine to service the connection. This approach provides a modular design pattern, but it also provides some limitations that make this design less desirable for Real-Time systems. Launching multiple servers can produce a significant hit in determinism and memory consumption, because each server needs to be instantiated and requires its own context space. Also, the fact that LabVIEW must then switch context between server instances creates an overall performance hit. The modularity benefits can quickly become performance burdens, particularly when implementing servers in lower-end targets such as FieldPoint or CompactRIO.

A more efficient approach uses a "Connection Manager" as the design centerpiece. The idea is to provide a mechanism to store and retrieve client connection information. The server application can simply retrieve client connections from the manager and service them in a loop. The following section describes how to use the connection manager included with the Simple TCP/IP Messaging component.

Back to Top

4. Connection Manager


The Connection Manager stores client connection parameters and provides the server with an API for accessing that information. The Connection Manager provides for storing application-specific context information on a per-connection basis, so that the server knows the properties and status for each connection that it services.

The Connection Manager programming interface is a VI that provides methods for adding, retrieving and closing connections, as well as setting and getting connection properties.


Figure 2. STM Connection Manager VI

The STM connection manager is implemented as a functional global that stores the following information:
- An array of TCP/IP client connection references
- An array of clusters for storing connection properties

These two arrays are correlated such that when the connection manager adds or deletes a connection reference, it does the same for the connection properties.

For the example application, the connection properties cluster contains the channel that the client wishes to view. The connection properties are defined using the strict typedef cluster stm_ConnectionProperties.ctl. You can redefine the connection properties for your application by modifying this typedef, which is located in a STM typedef subfolder in the LabVIEW user.lib folder.  You can open this typedef from the connection manager front panel. You may want to save a copy of the modified typedef so that it does not get overwritten if you reinstall the STM component.

The multi-client server design pattern uses a second VI (Check Connection) to check the status of each connection as it is serviced. If an error occurs, STM Check Connection determines whether it is recoverable. Errors such as timeout are considered warnings, while others, including "connection closed by peer" or "connection loss", indicate that the connection is no longer valid.



Figure 3. STM Check Connection VI

Figure 4 shows the STM Check Connection VI implementation.

 


Figure 4. STM Check Connection LabVIEW diagram


Note that STM Check Connection doesn't return an error if the incoming error code is 1, 62, 64 or 66 (these are standard TCP/IP error codes). These codes indicate that the connection has been closed for various reasons, and are expected to occur at some point during the execution. STM Check Connection traps these errors and calls the Connection Manager to closes the connection reference.

The following sections describe how to use the Connection Manager in a Command-based design pattern to create a multi-client server application.

 

Back to Top

5. Server Implementation Overview

This document focuses on the core implementation of the server VI. The other design pattern elements (High Priority Tasks, Initialize FIFOs, etc) are described in the command-based communication document.

A conceptual overview of the server design pattern is shown in Figure 5. The "Connection Manager" is the centerpiece of the design. It is responsible for adding new connections and passing client connections to the other loops.


Figure 5. Multi-client Server Block Diagram


Note that this design pattern is simply an extension of the command-based server. It just adds the "New Connection Monitor" and the "Connection Manager" elements. This approach allows you to convert existing code based on the command-based design pattern to a multi-client server.

The following sections describe the implementation of the functional blocks shown in Figure 5.

Back to Top

6. New Connection Monitor

The New Connection Monitor is responsible for handling new connections. It is implemented as an independent loop that sleeps until it receives a new connection request, so most of the time it doesn't use any CPU bandwidth.

When a connection request arrives, the new connection is stored in the connection manager, making it accessible by other loops. Figure 6 and the following comments describe the implementation of the New Connection Monitor loop.


Figure 6. New Connection Monitor


1. Create listener. Use the Create Listener VI to create a TCP listener for client network connections. Each client connection will use the port number with the client IP address to connect to the server. The operating system takes care of handling multiple connection requests for the same port.

Note: Port numbers below 1024 are generally reserved for operating system use.  Above that range it doesn't matter what port number you choose, as long as the port is not used by another application.

2. Wait for new client connection. The "TCP Wait On Listener" VI waits for new connections to arrive. The timeout value is set to wait forever (-1, default value), which causes the loop to sleep until a new connection arrives.  Note that the listener connection is routed outside this loop to another portion of the code. When the server application ends, its cleanup code closes the listener connection, which causes the "TCP Wait On Listener" to return with an error, which terminates the New Connection Monitor Loop.

3. Send Metadata. When a client connects, the server immediately sends it the connection metadata.

4. Add the new client to the connection manager. By adding the connection to the Connection Manager, we make it available to the rest of the application. In the example we don't set the connection properties at this point. As described later in this document, there is a command that the server responds to that allows setting connection properties. This approach allows the client to connect and then set its connection properties whenever it is ready to do so. Setting the connection properties immediately is also a valid approach. Use whatever approach fits your application.

The New Connection Monitor loop could be expanded to add functionality to the server. For example, you could set a maximum number of connections and reject incoming connections that exceed the maximum. Other improvement ideas include password protection, user privileges, and so on.

The New Connection Monitor controls connections dynamically, so it automatically provides a reconnect feature. Clients can disconnect and connect at any time. Because of this feature, the New Connection Monitor is useful even in applications that service just a single client.

Back to Top

7. Command Parser

As in the basic Command-based design pattern, the Command Parser is responsible for receiving commands from the clients and routing the information to the appropriate loop. Figure 7 and the following comments describe the Command Parser implementation.


Figure 7. Multi-client command parser

Notice that this version of the Command Parser is similar to the command-based design pattern example. The multi-client Command Parser adds a For loop that traverses and services each connection.

1. Retrieve all connections. Get the current list of client connections from the Connection Manager. The Connection Manager returns an array with all the connection references and a second array containing the connection properties. Reading directly from the Connection Manager ensures the connection list is current. If a connection is added, removed or modified anywhere in the server code, the next iteration of the Command Parser (or any other loop driven by the connection list) will automatically get the current list of connections. This implementation makes the design pattern very responsive to dynamic client connections and configuration changes.

2. Service each connection. The code inside the For Loop services each connection returned by the connection manager.

3. Poll all connections as quickly as possible. The STM Read Msg VI is normally a blocking function, meaning that it doesn't return unless a message has been received or it times out. The command-based design pattern uses the STM Read Msg VI to force the Command Parser loop to sleep until a new message from the client is received. In that scenario, the timeout approach works because there is only one client connected at a time. The Command Parser has nothing else to do until a new command is received, so it can sleep in between commands.

When servicing multiple clients, however, the "sleep" strategy does not work as well, because Commands arrive asynchronously and there is no way to know which connection will need servicing next. A better approach for multiple clients is to poll all of the connections as quickly as possible. Setting the STM Read Msg VI timeout to zero, causes it to time out immediately if no commands are pending for the current connection.

As discussed above, polling (unlike sleeping) can potentially consume a lot of CPU bandwidth, so we need to add sleep time to the command. We could add a small timeout to the "TCP Msg Read.vi", which would cap the command parser execution bandwidth, but make it less responsive as the number of connection grows. An alternate approach (as shown in Figure 7) is to use a timed loop to pace the execution of the Command Parser.

To summarize the Command Parser operation – Using the For loop, it traverses all the connections as quickly as possible.  Then using the timed loop it sleeps for a given amount of time. This technique preserves CPU time, while allowing the server to remain very responsive to multiple connections.

Note: The TCP/IP protocol buffers data for all connections. If the server is busy servicing a connection, it will not lose incoming messages on the other connections.

4. Check the connection status. If the STM Read Msg VI returns an error indicating that the connection has been closed, the STM Check Connection VI removes it from the Connection Manager and clears the error condition. The server code must handle any other error conditions, because they will cause the Command Parser to exit the While loop. The error conditions for each connection are merged outside the For loop and checked as the last step in the each timed loop iteration.

Note: Recoverable errors, such as timeout or connection loss are expected and are filtered by the STM Read Msg and Check Connection VIs.

5. Set connection properties. In this server example, the client can set its connection properties using the Set Channel command. The Command Parser unpacks the properties sent by the client and stores them in the Connection Manager properties array.

 

Back to Top

8. Medium Priority Tasks


As in the command-based design pattern, the Data Sender loop is a medium priority task. Figure 8 and the following comments describe the Data Sender implementation.


Figure 8. Data Sender

Notice that this version of the Data Sender is similar to the command-based design pattern example. The multi-client Data Sender adds a For loop that traverses and services each connection.

1. Retrieve all connections. Get the current connections and properties from the Connection Manager.

2. Service each connection. The code inside the For Loop services each connection returned by the Connection Manager.

3. Service according to the connection properties. The loop retrieves the properties for each connection. In this example, the server acquires data from 4 channels, but it only sends data from the channel identified by the Active Channel connection property. The High Priority Task returns data from all four channels as a one dimensional array in blocks of 1000 samples per channel. The data sender code indexes the array according to the Active Channel to extract the desired data block.

This section of the server application can be customized to perform specific actions required by your application. 

4. Check the connection status. If the STM Read Msg VI returns an error indicating that the connection has been closed, the STM Check Connection VI removes it from the Connection Manager and clears the error condition. The server code must handle any other error conditions, because they will cause the Command Parser to exit the While loop. The error conditions for each connection are merged outside the For loop and checked as the last step in the each timed loop iteration.

Back to Top

9. Conclusion

Distributed applications often require data sharing with multiple remote client applications. Server applications based on the approach presented here, are able to service multiple clients asynchronously, keeping track of individual configuration properties and providing dynamic disconnect and reconnect capability.

These concepts are an extension of the command-based design pattern, providing a robust, expandable and maintainable approach for building distributed applications. 

The multi-client design pattern can be used as a framework for building applications, but it is by no means a complete design. Features such as robust error handling must be added according to your application requirements.

You can download the STM component here.  The example described in this document is attached below.


Back to Top

Bookmark & Share

Downloads

Ratings

Rate this document

Answered Your Question?
Yes No

Submit