In the first part of this series of posts, we covered some of the basics of configuration in µC/TCP-IP. In this second and final part, we’ll examine window sizes and queues, both of which can affect the flow of data in and out of a device, and we'll also take a look at a unique tool that Micrium offers for analyzing a µC/TCP-IP-based system. The tool, µC/Probe, can help you ensure that your system has been configured optimally for the networks on which it will operate.
Configuring TCP Window Size
What happens when packets of data are sent to one of your sockets, but your application has not yet made any API calls to receive those packets? Similarly, what is the outcome when application code attempts to transmit a packet, but the underlying network controller is busy? In both cases, the behavior is shaped, at least partially, by queues. As the figure below indicates, every socket in a µC/TCP-IP-based system has both a receive and a transmit queue, and the capacities of these queues dictate the amount of data that can be stored either for subsequent application processing (in the case of the receive queue) or for transmission by the network controller (in the case of the transmit queue).
For developers whose applications are based entirely on UDP, queue sizes represent the only configurable parameters related to the pre-transmit and post-receive storage of data. With TCP, however, there are additional parameters to consider. That's because TCP (the protocol itself) provides flow control via windowing. Each TCP segment includes a window field that indicates the amount of buffer space the transmitting device has available for additional data. Within µC/TCP-IP, this advertised window value, which can be considered a receive window since it represents space set aside for incoming data, is accompanied by a transmit window, the value of which is expected to reflect the buffer space available for outgoing data.
By default, the receive and transmit windows in a µC/TCP-IP-based system are set equal to the receive and transmit queue sizes, respectively. The window sizes can be modified via the below definitions. These definitions are found in comments in the version of the
net_cfg.h configuration file provided with the stack, but they can be uncommented if changes are desired.
#define NET_TCP_DFLT_RX_WIN_SIZE_OCTET NET_SOCK_CFG_RX_Q_SIZE_OCTET
#define NET_TCP_DFLT_TX_WIN_SIZE_OCTET NET_SOCK_CFG_TX_Q_SIZE_OCTET
There are several factors to consider when setting the stack's window sizes. In the case of the receive window, the configured value advertises to other devices the amount of incoming data that a socket is able to accommodate, so this value should not exceed the total receive buffer space available to the stack. You can calculate your system's receive buffer space using the simple formula provided below:
Total Rx Buffer Space = Size of Rx Buffer * Nbr Rx Buffers
Unfortunately, it will never be the case, for a TCP-based device, that you'll be able to use all of your buffer space for TCP data. That's because TCP, like the protocols lying below it, requires data to be appended with header information, and this header information is ultimately stored in buffers. On Ethernet networks, µC/TCP-IP is typically configured so that each of a system's receive buffers has a size of 1518 Bytes, to reflect the largest possible Ethernet frame. However, as the calculations below indicate, at least 58 Bytes of this space is needed for headers, leaving only 1460 Bytes for actual TCP data.
TCP Max Payload = Interface Max (Ethernet 1518 Bytes) - Interface Header and Footer (Ethernet 18 Bytes) - Min IP Header (20 Bytes) - Min TCP Header (20 Bytes) = 1518 - 18 - 20 - 20 = 1460 Bytes
Window size calculations, then, should take into account the space required for header information. Another concern in such calculations is the number of sockets that will be managed by the system in question. A system's total buffer space is shared by its sockets, so a window size equal to that space – even when headers are considered – is generally not recommended. Instead, window size should be configured so that each socket will get a share of the available buffer space. The below formula, for receive window size, would meet this objective, dividing a system's buffer space evenly according to the number of connections using that space.
The denominator in this formula involves connections, rather than sockets, to reflect the stack's ability to buffer data that doesn't yet have a socket. This means data received in the (usually brief) interval of time between the establishment of a connection and the allocation of a socket for that connection. As the formula below indicates, the maximum number of connections existing in this state is determined by the configuration constant
Although the preceding two formulae both involve receive window size, the size of a system's transmit windows should likewise be predicated on the amount of available buffer space and the number of sockets in the system. (For transmit, the accept queue represented by
NET_SOCK_CFG_CON_ACCEPT_Q_SIZE_MAX would not need to be considered.) If you're looking to adjust your window size to better reflect your total buffer space and socket count, you can use the previously mentioned
NET_TCP_DFLT_RX_WIN_SIZE_OCTET #defines. You can also change window sizes on a per-connection basis via the API functions
With the function calls, it is possible to establish a relatively large window for one, presumably high-throughput connection and a much smaller window for another, lesser-used connection. The functions thus help in the efficient allocation of resources. When tweaking window sizes, however, you should make sure never to employ a value higher than the total usable buffer space that results from multiplying the previously provided maximum payload size (1460) by the number of buffers. You should also keep in mind that, for each of a TCP socket's two data paths (receive and transmit), there is a queue capacity as well as a window size, and, if the two are not equal, it is the smaller of the values that determines how much data can be stored.
A µC/TCP-IP Analysis Tool
To get feedback from your system as you're working to optimize window sizes and other configurable parameters, you can use the µC/TCP-IP Awareness package provided in Micrium's µC/Probe tool. As the videos on the µC/Probe section of Micrium's Web site indicate, it's very easy to get started with this tool. Once you're up and running, all that you need to do to utilize µC/TCP-IP Awareness with your current workspace is to simply select this package from the Workspace Explorer's Screens menu, which is shown in the below screenshot.
µC/Probe is typically used by dragging and dropping various graphical components (such as gauges, charts, and virtual switches), and then associating these components – via the tool's Symbol Browser – with an embedded system's variables. The resulting data screens become, essentially, a custom user interface to the system, allowing its variables to easily be read and written while code runs. Although the tool is an ideal companion for systems based on Micrium's embedded software modules, it is actually compatible with practically all embedded systems, and is not dependent on a particular operating system or software platform.
For developers who do choose Micrium's embedded software, one of the benefits of µC/Probe lies in its module-awareness capabilities, which include the µC/TCP-IP Awareness package. This package is a collection of pre-populated data screens that provide a means of monitoring much of µC/TCP-IP's internal data. The package's Overview screen, which is shown below, tracks the following:
- The number of TCP or UDP messages received or transmitted by the application
- The number of ARP, ICMP, and IGMP messages processed by the system
- The number of Ethernet frames processed by the system
- The number of Ethernet and Wi-Fi packets (as counted at the Physical layer) processed by the system
The statistics displayed in Overview are augmented by the contents of several other screens, each offering insight into a different aspect of the TCP/IP protocol suite:
- The TCP screen gives information about the number of active connections and connections in use, in addition to furnishing TCP-specific receive and transmit statistics.
- The UDP screen keeps track of datagram transactions as well as any UDP-related errors that occur within the stack.
- The ARP screen provides statistical information on ARP messages, resources consumed, and any cache errors.
- The IPv4 screen keeps a log of IP message statistics, and also displays relevant error counters.
- The Sockets screen displays connected sockets and allows the resource usage of these sockets to be monitored.
- The Connections screen has a list of connection resources along with notification of any connection-related errors.
- The Timers screen tracks timer-related resources and errors.
- The final Interfaces screen has pages for each of the attached system's link-layer interfaces. Within these sub-pages there are useful statistical graphs and charts portraying the inner operations of the stack.