Using TCP/IP Communication
Aurora Vision Studio has a set of filters for communication over the TCP/IP protocol. They are located in the Program I\O category of the Toolbox.
TCP/IP is actually a protocol stack, where TCP is on top of IP. These protocols are universally used from local to wide area networks and are fundamental to the communication over the Internet. They also constitute a basis for other protocols, like popular HTTP, FTP, as well as industrial standards Modbus TCP/IP or Modbus RTU/IP.
The TCP/IP protocol is a transport level communication protocol. It provides a bi-directional raw data stream, maintains a connection link and ensures data integrity (by keeping data in order and attempting to retransmit lost network packets). However raw TCP/IP protocol is usually not enough to implement safe and stable long term communication for a concrete situation and an additional communication protocol designed for a specific system must be formed on top of it. For example, when implementing a master-slave commands system with correct command execution check a command and acknowledge transaction must be used (first sending a command, than receiving with a timeout a command acknowledge where the timeout expiration informs that the command was not processed correctly). Single send operation is not able to detect whether data was properly received and processed by a receiver, because it is only putting data into a transmission buffer. Other situations may require different approach and usually required protocol will be imposed by a system on the other side of the connection. Because of the above basic TCP/IP and communication protocols knowledge is required to properly implement a system based on TCP/IP.
The communication is made possible with the use of Sockets. A network socket is an abstract notion of a bidirectional endpoint of a connection. A socket can be written to or read from. Data written to a socket on one end of the connection can be read from the opposite end. The mechanism of sockets is present in many programming languages, and is also the tool of choice for general network programming in Aurora Vision Studio.
Establishing a Connection
To communicate using the TCP/IP protocol stack, first a connection needs to be established. There are two ways of doing this: (1) starting a connection to a server and (2) accepting connections from clients. The sockets resulting from both of the methods are later indistinguishable - they behave the same: as a bidirectional communication utility.
A connection, once created, is accessed through its socket. The returned socket must be connected to all the following filters, which will use it for writing and reading data, and disconnecting.
Usually, a connection is created before the application enters its main loop and it remains valid through multiple iterations of the process. It becomes invalid when it is explicitly closed or when an I/O error occurs.
Connects to a specific port on a remote host.
The party to perform this operation is the client.
Opens a local TCP port and waits for incoming connections.
The party to perform this operation is the server.
Writing Data to Sockets
The filters for sending data take a SocketId value, which identifies an open connection and data, which is to be transferred to the other endpoint. The data can be of three different kinds: textual, binary or serialized objects.
The write operation is usually fast, but it can become problematic, when the other party is too slow in consuming the data, or if we attempt to write data faster than the available network throughput. The write operation is limited to putting data into a transmission buffer, without waiting for data delivery or receive confirmation. When data are being written faster than they can be delivered or processed by a receiver the amount of data held in a transmission buffer will start growing, causing significant delay in communication, and eventually the transmission buffer will overflow causing the write operation to rise and error.
Writes plain text in UTF-8 encoding to a socket.
An optional suffix* can be appended to the text.
|Writes arbitrary binary data, given as a ByteBuffer, to a socket.
|Writes a serialized object to a socket. The resulting data can only be read in another instance of Aurora Vision Studio/Executor with the TcpIp_ReadObject filter described below, and with using the same instantiation type.
Reading Data from Sockets
Reading data requires an open connection and can return the received data in either of ways:
The time necessary to receive data can be dependent on the network RTT (round-trip time), transfer bandwidth and amount of received data, but much more on the fact, whether the other side of the connection is sending the data at all. If there is no data to read, the filter has to wait for it to arrive. This is, where the use of the inTimeout parameter makes the most sense.
The filters for reading can optionally inform that the connection was closed on the other side and no more data can be retrieved. This mechanism can be used when connection closing is used to indicate the end of transmission or communication. The outEof output is used to indicate this condition. When the end of stream is indicated the socket is still valid and we should still close it on our side.
Reads text in UTF-8 encoding until reaching a specified delimiter.
The delimiter* can be either discarded, returned at the end of output, or left in the buffer to be processed by a subsequent read operation.
|Reads a fixed-length chunk of binary data.
|Reads a serialized object. The data can only come from another instance of Aurora Vision Studio/Executor executing the TcpIp_WriteObject filter described above, using the same type parameter.
|Reads all text, until EOF (until other side closes the connection).
|Reads all data, until EOF (until other side closes the connection).
* - the delimiter and the suffix are passed as escaped strings, which are special because they allow for so called escape sequences. These are combinations of characters, which have special meaning. The most important are "\n" (newline), "\r" (carriage return), and "\\" - a verbatim backslash. This allows for sending or receiving certain characters, which cannot be easily included in a String.
Closing Connections after Use
When a give socket is not needed anymore it should be passed to the socket closing filter, which will close the underlying connection and release the associated system resources. Every socket should be explicitly closed using the socket closing filter, even when the connection was closed on the other side or the connection was broken.
Typically, connections are being closed after the application exits its main loop macrofilter.
|Closes the connection gracefully and releases the socket.
The most typical application structure consist of three elements:
- A filter creating a socket (TcpIp_Connect or TcpIp_Accept) executed.
- A macrofilter realizing the main loop of the program (task).
- A filter closing the socket (TcpIp_Close).
If the main loop task may exit due to an I/O error, as described in the next section, there should be also a fourth element, a Loop filter (possibly together with some Delay), assuring that the system will attempt reconnection and enters the main loop again.
For more information see the "IO Simple TcpIp Communication" official example.
Error Handling and Recovery
In systems where an error detection is critical it is usually required to implement communication correctness checks explicitly based on used communication protocol and capabilities of a communication peer. Such checks will usually consists of transmitting operation acknowledge messages and receiving expected transmissions with limited time. To implement this a timeout system of receiving filters can be used.
Additionally all TCP/IP filters can notify about unexpected situations or internal communication errors by rising an IoError. Such errors might result out of connection closing by a peer (when it is not expected in a given filter), system errors, network errors, or a broken connection state. The broken connection is usually the most common source of a communication error. Connection enters a broken state as a result of underlying system detecting that the other side of the communication is not confirming that it is receiving data (in some system defined time). After the system marks the connection as broken the next TCP/IP filter (that attempts to use this connection) will notify about that by rising an IoError. Relaying on detecting a broken connection is usually not the most reliable way of verifying valid connection state in error critical systems, as its detection may take a long time, detection time may vary on different systems and in some situations (like uni-directional transmission) it may not be detected at all. The TCP/IP Keep-Alive mechanism (available to activate in TcpIp_Connect and TcpIp_Accept) may help in increasing chances of detecting a broken connection.
IoError raised by filters can be handled using the macrofilter Error Handling mechanism (by putting TCP/IP filters into a separate Task). When a TCP/IP filter is terminated by an error its underlying connection is left in an undefined state, the connection should not be used anymore and the socket should be closed with a TcpIp_Close filter. The application can then attempt to recover from TCP/IP errors by initializing the connection from scratch. Specific possibilities and requirements of recovering from communication errors depends on a used protocol and a communication peer capabilities.
Using Tcp/Ip Communication with XML Files
Very often Tcp/Ip communication is used to exchange information structured as XML documents. For this type of communication, use filters for reading or writing text together with the filters from the System :: XML category for parsing or creating XML trees.
Example: Reading Data from HTTPThis example consists of the following steps:
- A connection is made, to port 80 (the HTTP standard port) of www.adaptive-vision.com host.
- A HTTP query string is sent, to request an image from a specific URL.
- The headers, which end with "\r\n\r\n" (double newline) are read (and not processed).
- All the remaining data - the HTTP response body - is read and returned as binary data.
- The bytes are converted to an image, using a utility function.
|Previous: Technical Issues
|Next: General Image Acquisition