Quanser Stream C API End of trail navigation bar

Table of Contents > QUARC > User's Guide > QUARC External Interfaces > C Functions > Communications

Persistent Stream API

Persistent Stream API

The Quanser Persistent Stream API is used to create communication channels between C/C++ based applications and the real-time code generated for a Simulink model. The Persistent Stream API includes high-level C functions to create server/client applications which support persistent connectivity. This external interface is implemented using the Quanser Stream API and is meant to be used for basic communications. For more information about the QUARC Stream API, please refer to Stream API section.

In fact, the Stream Server and Stream Client blocks in the QUARC Targets/Communications/Basic library use the Persistent Stream API to operate within the MATLAB environment.

For details of these two blocks, please refer to Stream Server and Stream Client blocks reference pages.

Persistent connectivity means that the server/client will attempt to re-establish the connection when it is closed or lost. The Persistent Stream API is particularly valuable in applications like the deployment of cooperating UAVs (unmanned aerial vehicles), where you wish to communicate whenever another vehicle is within range. The Persistent Stream API takes the complexity out of maintaining the connection; you just send or receive data whenever there is a connection established. In addition, the Persistent Stream API is a high-level interface which does not require an extensive knowledge of communications.

Quanser software (e.g., QUARC, RCP) supports a variety of common communications protocols which are all accessed via the Quanser Persistent Stream API functions. The Persistent Stream API contains functions to connect to a remote host and to listen for client connections. For a complete list of the functions included in the Persistent Stream API, you can refer to the Persistent Stream API header file, .

The Persistent Stream API is designed to support any kind of communications channel. This flexibility is provided by loading protocol-specific drivers to handle a particular communications protocol. Hence, new communications protocols may readily be added without any changes to the functions of the Persistent Stream API. However, this flexibility requires that the Persistent Stream API have a means of specifying the communications protocol and any protocol-specific options. The protocol and options are provided through the use of Universal Resource Identifiers or URIs. For more information about URIs and the communications protocols supported by QUARC, please refer to Quanser Communications Protocols section of the QUARC documentation.

The URI is used when the communication channel is first established. To change the protocol in use, only the URI needs to change. For instance, consider an application using shared memory. In order to change the protocol to TCP/IP, all you need to do is to change the URI from shared memory to TCP/IP.

Warning The user needs to be careful when using the UDP protocol. Be sure to read the documentation on the UDP protocol thoroughly, particularly on the different peer options. In particular, note that UDP is a datagram protocol and not a stream protocol, unlike TCP/IP. Hence, it is a connectionless protocol. Thus, it does not "listen" for client connections like the TCP/IP protocol. Therefore, listening for client connections does not establish a "listening" socket but simply results in a placeholder stream.

Warning Likewise, "accepting" a client connection using UDP creates a socket that is bound to the port specified by the URI used for listening and returns immediately. In general, the peer to whom datagrams should be sent is established by the first client from whom a UDP datagram is received. Thus, for server connections, a receive operation should always be done before the first send operation.

Warning Similarly, connecting to the server on the client side using UDP does not actually "connect" to a remote host. Instead, the client sets the address that will be used when sending packets. The first time a packet is sent the socket becomes implicitly bound to that address. Hence, in general, when using UDP protocol for communications on the client side, a send operation should always be before the first receive operation.

The Persistent Stream API operates in non-blocking mode, meaning that its functions will always return immediately. However, just like the Basic Stream blocks, it has two implementations that it supports internally. It can use non-blocking I/O, in which case it is single-threaded and uses non-blocking I/O for its communications, or it can create separate threads which do blocking I/O but communicate with the send/receive functions of the API via a FIFO queue so that the send/receive functions are still non-blocking, even though the actual I/O is done in a blocking thread. The reason for these two implementations is that some communications protocols do not support non-blocking I/O. Also, the multithreaded implementation tends to reconnect faster if the connection to the peer is lost and allows the pstream_send and pstream_receive functions to execute faster, since they need only check for items in their associated FIFO queues. Since the send/receive functions are always non-blocking, the C code for using both implementations is the same except for one of the options of the persistent stream used for communications.

The mode of operation only dictates the way functions or blocks work in a particular application or model. Therefore, it is possible to have a server operating in blocking mode and a client operating in non-blocking mode and vice versa.

This section serves as a guide on how to use the Quanser Persistent Stream API to establish connections between C/C++ applications and real-time code. The C/C++ based application can be acting as either a client trying to connect to the real-time code or as a server accepting connections from the real-time code. We will demonstrate both cases in this section. Since there is a not a significant change in the coding when changing the mode of operation, we will only demonstrate one mode of operation in each case. Before going into details, a few steps are required to setup your C/C++ application to use the Persistent Stream API. Please use the following list to refer to each topic:

Setting Up the C/C++ Application to Use the Persistent Stream API

The user needs to follow a sequence of steps to setup the C/C++ application to use the Persistent Stream API. We assume that Microsoft Visual Studio 2005 or later is being used to develop the application. Please perform the following procedure to setup your application, once in the Microsoft Visual Studio environment:

  1. Open the Property Pages window by right-clicking on the project containing your application and choosing the Properties menu item in the context menu. The following figure illustrates this window.

    Property Pages Window

  2. In the Property Pages window, click on the C/C++ pane. The first parameter in this pane is called Additional Include Directories. Type in $(QSDK_DIR)include in its field as shown in the figure below. Note that QSDK_DIR is an environment variable referring to the directory in which QUARC has been installed.

    Property Pages Window

  3. Click on the Linker pane and expand its treeview. In the General section of this pane, there is a parameter called Additional Library Directories. For the Win32 platform, type in $(QSDK_DIR)lib\windows in its field as shown in the figure below. For the x64 platform, use $(QSDK_DIR)lib\win64 instead, since the code must be linked with 64-bit versions of the QUARC libraries in that case.

    Property Pages Window

  4. While in the Linker pane, click on the Input section. The first parameter in this section is called Additional Dependencies. Type in quanser_communications.lib quanser_runtime.lib quanser_common.lib in its field as illustrated in the following figure. Note that the entries in the field should be separated by one space.

    Property Pages Window

  5. Once all these changes are made, click on Apply and then OK.

C/C++ Application as Client

The C/C++ application may act as a client connecting to the real-time code. In this case, the real-time code functions as the server. In this section, one Simulink model is used to demonstrate the server side and one C++ application is used to demonstrate the client side.

Simulink Server Model

The example model used in this section is illustrated by the following figure.

Example Simulink Model

The Stream Server block acts as the server in the communication process. It listens for and accepts a connection from a local or remote host and sends and/or receives data from that host. This block is a high-level block and does not require a sophisticated knowledge of communications to be used. For more information about this block, please refer to the Stream Server block help page. You can also refer to the Basic Communications section of the QUARC documentation in MATLAB to gain more insight on how to use this block. Even though the intermediate or advanced blocks are more flexible, we will not demonstrate how to use them in this example for the sake of simplicity. Information on how to use the intermediate or advanced communications blocks is provided in the Communications section of the MATLAB documentation for QUARC.

The data to be sent by the server model is a sine wave generated by a Sine Wave block. The data received by the server model is what is sent by the client application. In our case, the client/server system acts as a loop-back system. The data sent to the client gets sent back to the server by that client. In order to check the latency of our system, the data to be sent is plotted on a scope along with the actual received data from the client. The data sent is passed through a Memory block in order to compensate for the fact that the block operates at a fixed sample rate so the data received will not be available until the sampling instant immediately after it is received. In the ideal case, the two curves will be on top of each other, indicating that the latency is less than one sampling instant. Due to limited communications bandwidth, thread context switching latency and other factors, there will always be some latency in communications, although it will not be noticeable if the latency is less than one sampling instant.

There are a few parameters of the Stream Server block to be set. The following figure shows the Stream Server block properties window with parameters set accordingly.

Stream Server Block Properties

Notice the field highlighted in blue. This field determines the mode of operation. You may choose either mode. By choosing the blocking mode, four other fields get enabled, namely Send FIFO size in samples, Receive FIFO size in samples, Send thread priority and Receive thread priority. Use the default values for these parameters. For more information on these parameters, you can refer to the Stream Server block help page in MATLAB. If you choose non-blocking mode, those four parameters will be disabled. Another important parameter to consider is the URI upon which to listen. It is set to shmem://foobar:1 which indicates that the server listens on the shared memory buffer called "foobar" at port number 1.

Client Application

Before going into the details of the source code, remember to include the header files containing the functions used in your code. For the Persistent Stream API, you need to include the "quanser_persistent_stream.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include <signal.h>
#include "quanser_messages.h"
#include "quanser_thread.h"
#include "quanser_persistent_stream.h"

The first section of the source code is dedicated to variable definitions and initialization. The following code segment shows this section.

#include "stdafx.h"

static int stop = 0;

static void control_c_handler(int signum) {
	stop = 1;
}

int _tmain(int argc, _TCHAR* argv[]) {
	const char   uri[]  = "shmem://foobar:1";
	const char * locale = NULL;
		
	/* The persistent stream for the client */
	t_pstream client;
	
	/* The options structure for the persistent stream */
	t_pstream_options options;
	
	/* The double variable used for sending/receiving of data */
	t_double buffer;

	char message[512];
	t_error result;

	/* These two instances of the qthread_attr_t structure refer to the send/receive thread attributes */
	qthread_attr_t receive_thread_attributes;
	qthread_attr_t send_thread_attributes;

	/* Register a Ctrl+C handler to stop the code upon Ctrl+C being pressed */
	signal(control_c_handler);

	/* The above instances are initialized here. */
	qthread_attr_init(&receive_thread_attributes);
	qthread_attr_init(&send_thread_attributes);
	
	/*
		The persistent stream's send/receive thread attributes are set to the above created instances.
		We could set these fields to NULL to use the default thread attributes and avoid using qthread_attr_t
		variables but we show the use of the qthread_attr_t to demonstrate how thread attributes may be used.
		The functions for setting thread attributes are in 
	*/
	options.receive_thread_attributes = &receive_thread_attributes;
	options.send_thread_attributes = &send_thread_attributes;
	
	/* Number of units to be sent/received with each pstream_send or pstream_receive call */
	options.num_receive_units = 1;
	options.num_send_units = 1;
	
	/* Size of each unit to be sent. */
	options.receive_unit_size = sizeof(t_double);
	options.send_unit_size = sizeof(t_double);
	
	/* Send/receive buffer sizes */
	options.receive_buffer_size = 1000;
	options.send_buffer_size = 1000;
	
	/* The FIFO queue buffer sizes. These FIFO queues will be used if PSTREAM_FLAG_MULTITHREADED is set. */
	options.send_fifo_size = 1000;
	options.receive_fifo_size = 1000;
	
	/* The persistent stream flags. */
	options.flags = 0;
	
	/*
		Use blocking I/O. The pstream API is always non-blocking, but setting this flag will create
		a separate thread in which blocking I/O is done. But that thread communicates with the 
		pstream_send and pstream_receive via the send/receive FIFO queues. The pstream_send and 
		pstream_receive are always non-blocking and setting this flag will not change their mode 
		of operation.
	*/
	options.flags |= PSTREAM_FLAG_MULTITHREADED;
	
	/* Set the other flags accordingly. Since we are looking at latency, use the most recent flags */
	options.flags |= PSTREAM_FLAG_MINIMIZE_LATENCY | PSTREAM_FLAG_SEND_MOST_RECENT | PSTREAM_FLAG_RECEIVE_MOST_RECENT;

	/* The size of the options instance */
	options.size = sizeof(options);

The uri variable is used by the pstream_connect C function to connect to the server using the specified URI. We have set this value to the URI configured in the Stream Server block. The options variable is an instance of the t_pstream_options structure, which contains the options for the client persistent stream. The receive_thread_attributes and send_thread_attributes instances are initialized and then used as send/receive thread attributes of the options structure. The other variables of this structure are all set accordingly. Please refer to each line's comment for more information.

One of the most important variables of the options structure is an unsigned integer called flags. If you look at the code, an OR operation is performed on this variable with the PSTREAM_FLAG_MULTITHREADED constant to indicate that blocking I/O is used as the mode of operation. In case you want to change the mode to non-blocking, you should perform an OR operation on this variable with the PSTREAM_FLAG_NON_BLOCKING constant. There are other constants added to the flags field which set different options for the persistent client stream.

The next section is where the client tries to connect to the server and upon connecting, sends and receives data. It is illustrated by the code segment below.

	/*
		This function attempts to connect to the server side using the specified URI.
		The pstream API is non-blocking. Therefore, this function will return immediately.
	*/
	result = pstream_connect(uri, &options, &client);
	
	if (result == 0) /* now connected */
	{
		buffer = 0.0;

		/* The loop where sending/receiving of data takes place. */
		while (!stop)
		{
			/* This function receives data from the server. It is always non-blocking. */
			result = pstream_receive(client, &buffer);
			if (result > 0)
			{   
				/* This function sends data to the server side and is also non-blocking */
				result = pstream_send(client, &buffer);
			}
			else if (result == -QERR_CONNECTION_NOT_BOUND) /* if UDP and socket not bound yet */
			{
				/* 
					This case is not required for most protocols. However, it is required for UDP when the pstream_receive
					is done first, as it is in this example. The reason is that the pstream_connect does not explicitly bind
					the UDP socket to the port, as the server does, because otherwise the client and server could not be run
					on the same machine. Hence, the pstream_connect implicitly binds the UDP socket to the port when
					the first pstream_send operation is performed. Thus, a pstream_send should always be executed prior to the
					first pstream_receive for the UDP protocol. Otherwise, the -QERR_CONNECTION_NOT_BOUND error is returned.
					Therefore, because this example does the pstream_receive first, we check for the -QERR_CONNECTION_NOT_BOUND
					error and issue a pstream_send in this instance. For other protocols, this case is never invoked, but
					the code is included here so that the protocol can be changed to UDP and the example left unchanged.
				*/
				result = pstream_send(client, &buffer); /* Send a value to get connection bound */
			}
			/*
				Since the persistent stream maintains the connection, even reconnecting if necessary, then
				we don't really need to process the return value. We can continue to use the t_pstream even
				when errors are returned.

				However, since the pstream_send and pstream_receive functions are non-blocking we should
				really do other work or go to sleep so that we do not hog 100% of the CPU time.
			*/
		}

		/* If Ctrl+C is pressed, close the client stream. */
		pstream_close(client);
		
		/* Print any error that occurred while trying to send/receive */
		if (result &lt; 0) {
			msg_get_error_messageA(locale, result,message, sizeof(message));
			printf("Error communicating on URI '%s'. %s\n", uri, message);
		}
	}
	/* In case of errors in attempting to connect to the server, print the appropriate message. */
	else
	{
		msg_get_error_messageA(locale, result, message, sizeof(message));
		printf("Unable to connect to URI '%s'. %s\n", uri, message);
	}
	printf("Please press any key to exit...\n");
	getchar();  
	return 0;
}

The pstream_connect function tries to connect to the server using the given URI. Since Persistent Stream API is always non-blocking, this function will return immediately. If there are any errors in the attempt, it will return a negative error code. Otherwise, a value of zero is returned. In case the function would block if it was operating in blocking mode, the -QERR_WOULD_BLOCK error code is returned. The communication process should start only if a value of zero is returned by this function.

The while loop is where sending/receiving takes place. The pstream_receive function is used to receive data from the client stream's receive buffer. It is non-blocking and returns a positive value with successful execution. The order of executing functions is important in non-blocking mode. That is why we have used an if statement to send the received data back to the server model using the pstream_send function. In case of errors in any of the mentioned operations, the program exits the while loop. Since the persistent stream maintains the connection, even reconnecting if necessary, then we do not really need to process the return value. We can continue to use the t_pstream even when errors are returned. The only error case being checked is whether an error code of -QERR_CONNECTION_NOT_BOUND is returned. This case is not required for most protocols. However, it is required for UDP when the pstream_receive is done first, as it is in this example. The reason is that the pstream_connect does not explicitly bind the UDP socket to the port, as the server does, because otherwise the client and server could not be run on the same machine. Hence, the pstream_connect implicitly binds the UDP socket to the port when the first pstream_send operation is performed. Thus, a pstream_send should always be executed prior to the first pstream_receive for the UDP protocol. Otherwise, the -QERR_CONNECTION_NOT_BOUND error is returned. Therefore, because this example does the pstream_receive first, we check for the -QERR_CONNECTION_NOT_BOUND error and issue a pstream_send in this instance. For the shmem protocol, this case is never invoked, but the code is included here so that the protocol can be changed to UDP and the example left unchanged.

You can now build your Simulink model and the client application. To start the communication process, always remember to run the server side before the client side. Running the client before the server would cause the program to produce an error and exit. Therefore, you should run the Simulink model first and then the client application. Once the communication process starts, you can stop the server model and restart it again to verify the persistent connectivity of your application. You can look at the "Data Received" Scope block in the server model to see the plots of data received from the client and the data sent by the server which should be the same.

Warning You can have any combination of operation modes for the server/client system. For instance, you can change the client's mode of operation to non-blocking while keeping the server in blocking mode and obtain the same results.

C/C++ Application as Server

The C/C++ application could be acting as a server accepting connections from the real-time code. In this case, the real-time code serves as the client. In this section, one Simulink model is used to demonstrate the client side and one C++ application is used to demonstrate the server side.

Simulink Client Model

The example model used in this section is illustrated by the following figure.

Example Simulink Model

The Stream Client block acts as the client in the communication process. This block establishes a "persistent" connection with a host. The host may be local or remote. If the connection to the host is lost, this block automatically attempts to reconnect to the host. The Stream Client block is a high-level block and does not require a sophisticated knowledge of communications to be used. For more information about this block, please refer to the Stream Client block help pagein the MATLAB help for QUARC. You can also refer to the Basic Communications section of the QUARC documentation to gain more insight on how to use this block. Even though the intermediate or advanced blocks are more flexible, we will not demonstrate how to use them in this example for the sake of simplicity. Information on how to use the intermediate or advanced communications blocks is provided in the Communications section.

The data to be sent by the client model is a sine wave generated by a Sine Wave block. The received data is what is sent by the server application. In our case, the client/server system acts as a loop-back system. The data sent to the server gets sent back to the client by that server. In order to check the latency of our system, the data to be sent is plotted on a scope along with the actual received data from the server. The data sent is passed through a Memory block in order to compensate for the fact that the block operates at a fixed sample rate so the data received will not be available until the sampling instant immediately after it is received. In the ideal case, the two curves will be on top of each other, indicating that the latency is less than one sampling instant. Due to limited communications bandwidth, thread context switching latency and other factors, there will always be some latency in communications, although it will not be noticeable if the latency is less than one sampling instant.

There are a few block properties for the Stream Client block to be set. The following figure shows the Stream Client block properties window with parameters set accordingly.

Stream Client Block Properties

Note that the blue highlighted field is where you choose the mode of operation. We have set this parameter to Use non-blocking I/O to indicate that the non-blocking I/O implementation is to be used. If you wanted to change the mode to blocking, you would have to set this parameter to Use blocking I/O in a separate thread. Notice that the first parameter, URI of host to which to connect, is set to udp://localhost:18000. The URI is modified to change the communications protocol from the one used in the previous section to UDP protocol. As mentioned at the beginning of this document, a send operation should be performed first on the client side to bind the socket when using the UDP protocol. However, binding the socket is performed internally in the Stream Client block. For information on other parameters for this block, please refer to the Stream Client block reference page in the MATLAB help for QUARC.

Server Application

Before going into the details of the source code, remember to include the header files containing the functions used in your code. For the Persistent Stream API, you need to include the "quanser_persistent_stream.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include <signal.h>
#include "quanser_messages.h"
#include "quanser_thread.h"
#include "quanser_persistent_stream.h"

The first section of the source code is dedicated to variable definitions and initialization. The following code segment shows this section.

#include "stdafx.h"

static int stop = 0;

static void control_c_handler(int signum) {
	stop = 1;
}

int _tmain(int argc, _TCHAR* argv[]) {
	const char   uri[]  = "udp://localhost:18000";
	const char * locale = NULL;
		
	/* The persistent stream for the server */
	t_pstream server;
	
	/* The options structure for the persistent stream */
	t_pstream_options options;
	
	/* The double variable used for sending/receiving of data */
	t_double buffer;

	char message[512];
	t_error result;

	/* These two instances of the qthread_attr_t structure refer to the send/receive thread attributes */
	qthread_attr_t receive_thread_attributes;
	qthread_attr_t send_thread_attributes;

	/* Register a Ctrl+C handler to stop the code upon Ctrl+C being pressed */
	signal(SIGINT, control_c_handler);

	/* The above instances are initialized here. */
	qthread_attr_init(&receive_thread_attributes);
	qthread_attr_init(&send_thread_attributes);
	
	/*
		The persistent stream's send/receive thread attributes are set to the above created instances.
		We could set these fields to NULL to use the default thread attributes and avoid using qthread_attr_t
		variables but we show the use of the qthread_attr_t to demonstrate how thread attributes may be used.
		The functions for setting thread attributes are in 
	*/
	options.receive_thread_attributes = &receive_thread_attributes;
	options.send_thread_attributes = &send_thread_attributes;
	
	/* Number of units to be sent/received with each pstream_send or pstream_receive call */
	options.num_receive_units = 1;
	options.num_send_units = 1;
	
	/* Size of each unit to be sent. */
	options.receive_unit_size = sizeof(t_double);
	options.send_unit_size = sizeof(t_double);
	
	/* Send/receive buffer sizes */
	options.receive_buffer_size = 1000;
	options.send_buffer_size = 1000;
	
	/* The FIFO queue buffer sizes. These FIFO queues will be used if PSTREAM_FLAG_MULTITHREADED is set. */
	options.send_fifo_size = 1000;
	options.receive_fifo_size = 1000;
	
	/* The persistent stream flags. */
	options.flags = 0;
	
	/*
		Use non-blocking I/O. The pstream API is always non-blocking and setting this flag will 
		indicate that non-blocking I/O be used with no threads being created for communications.
	*/
	options.flags |= PSTREAM_FLAG_NON_BLOCKING;
	
	/* Set the other flags accordingly. Since we are looking at latency, use the most recent flags */
	options.flags |= PSTREAM_FLAG_MINIMIZE_LATENCY | PSTREAM_FLAG_SEND_MOST_RECENT | PSTREAM_FLAG_RECEIVE_MOST_RECENT;

	/* The size of the options instance */
	options.size = sizeof(options);

The uri variable is used by the pstream_listen C function to listen to client connections on the specified URI. We have set this value to the URI configured in the Stream Client block. The options variable is an instance of the t_pstream_options structure, which contains the options for the server persistent stream. The receive_thread_attributes and send_thread_attributes instances are initialized and then used as send/receive thread attributes of the options structure. The other variables of this structure are all set accordingly. Please refer to each line's comment for more information.

One of the most important variables of the options structure is an unsigned integer called flags. If you look at the code, an OR operation is performed on this variable with the PSTREAM_FLAG_NON_BLOCKING constant to indicate that non-blocking I/O is used as the mode of operation. In case you want to change the mode to blocking, you should perform an OR operation on this variable with the PSTREAM_FLAG_MULTITHREADED constant. There are other constants added to the flags field which set different options for the persistent server stream.

The next section is where the server listens for client connections and upon establishing a connection, sends/receives data. It is illustrated by the code segment below.

	/*
		This function listens for new client connections using the specified URI.
		The pstream API is non-blocking. Therefore, this function will return immediately.
	*/
	result = pstream_listen(uri, &options, &server);
	
	if (result == 0)/* Now connected. */
	{   
		
		/* The loop where sending/receiving of data takes place. */
		while(!stop)
		{
			/* This function receives data from the client. It is always non-blocking. */
			result = pstream_receive(server, &buffer);
			if (result > 0)
			{   
				/* This function sends data to the client side and it is non-blocking. */
				result = pstream_send(server, &buffer);
			}
				   
			/*
				Since the persistent stream maintains the connection, even reconnecting if necessary, then
				we don't really need to process the return value. We can continue to use the t_pstream even
				when errors are returned.

				However, since the pstream_send and pstream_receive functions are non-blocking we should
				really do other work or go to sleep so that we do not hog 100% of the CPU time.
			*/
		}

		/* If Ctrl+C is pressed, close the server stream. */
		pstream_close(server);
		
		/* Print any error that occurred while trying to send/receive */
		if (result < 0) {
			msg_get_error_messageA(locale, result,message, sizeof(message));
			printf("Error communicating on URI '%s'. %s\n", uri, message);
		}
	}
	/* In case of errors in attempting to listen for client connections, print the appropriate message. */
	else
	{
		msg_get_error_messageA(locale, result, message, sizeof(message));
		printf("Unable to listen on URI '%s'. %s\n", uri, message);
	}
	printf("Please press any key to exit...\n");
	getchar();  
	return 0;
}

The pstream_listen function listens for and accepts new client connections using the given URI. Since Persistent Stream API is always non-blocking, this function will return immediately. If there are any errors while listening, it will return a negative error code. Otherwise, a value of zero is returned. In case the function would block if it was operating in blocking mode, the -QERR_WOULD_BLOCK error code is returned. The communication process should start only if a value of zero is returned by this function.

The while loop is where sending/receiving takes place. The pstream_receive function is used to receive data from the connection stream's receive buffer. It is non-blocking and returns a positive value with successful execution. The order of executing functions is important in non-blocking mode. That is why we have used an if statement to send the received data back to the client model using the pstream_send function. Since the persistent stream maintains the connection, even reconnecting if necessary, then we do not really need to process the return value. We can continue to use the t_pstream even when errors are returned. In case of errors occurring while sending/receiving data, an appropriate message is printed. The server will continue to listen for new connections. Same situation applies to the case where the client side has been shut down. If any errors occur while trying to execute the pstream_listen function, the appropriate message describing the nature of the error is printed, the server stream is closed and the user is prompted to press any key to exit the application.

You can now build your Simulink model and the server application. To start the communication process, always remember to run the server side before the client side. Running the client before the server would cause the program to produce an error and exit. Therefore, you should run the server application first and then the Simulink model. Once the communication process starts, you can look at the "Data Received" Scope block in the server model to see the plots of data received from the server and the data sent by the client which should be the same.

Warning As mentioned at the beginning of this document, the user needs to be careful when using the UDP protocol. A send operation should be performed prior to receiving on the client side to bind the socket. However, since the Stream Client takes care of this ordering internally, all you need to do to change the protocol in use is to change the URI format.

Warning You can have any combination of operation modes for the server/client system. For instance, you can change the server's mode of operation to blocking while keeping the client in non-blocking mode and obtain the same results.

 

navigation bar