Traditionally, any user-level application that needed network access had two choices: either the TCP or the UDP protocol stacks. Both are a standard part of all Unix networking packages, and their interfaces follow strict guidelines. TCP and UDP are both implemented on top of the IP layer. This means that once a packet is encapsulated (and possibly fragmented), it is passed to the IP stack for further processing. Both TCP and UDP support nonblocking operation and asynchronous notification upon delivery. These characteristics are perhaps their only similarities, for these two protocols are targeted at different audiences.
The TCP stack presents a reliable, connection-oriented data stream to the application. It accomplishes complete reliability through various techniques, most of which are beyond the scope of this paper. Of particular interest to us here is the amount of overhead caused by each. Internal buffering, dynamic memory allocation, timeouts for both packets and buffers, state tables, and explicit control messages all require extra cycles to manage and are responsible for a significant amount of delay. Each send performed using a deadlock-free protocol requires three trips over the network before the reader can continue (<- request to send, -> ready-to-receive, <- data and -> acknowledge). Also, each trip requires a message of different composition. Connections to each host must established in advance and subsequently kept active, placing further demand on the kernel and its resources. TCP was designed for communicating data streams and does not preserve message boundaries, whereas distributed computing often involves infrequent and irregular communication of discrete messages. Explicit state of the interface would have to be maintained to prevent the user from receiving control messages. Otherwise, incoming messages would have to be scanned for headers and processed piecemeal. On many PCs and workstations, using TCP can severely limit an application's network throughput. The performance one achieves is frequently limited by the power of the CPU and not by the networking device or the speed of the medium. For our purposes, its level of functionality is largely unnecessary, especially considering our assumption of a low rate of packet loss.
The UDP stack is a connectionless, unreliable, datagram-oriented service. In fact, UDP adds very little to IP's functionality. Its only additions are an optional checksum of the data, a larger packet size (often 4k), and the ability to be used by non-root users. UDP's overhead is higher than IP's, primarily due to the computation of the checksum and the subsequent formation of the header. For the situation where portability is of the utmost importance and root access is not practical, an AM implementation using a UDP-like protocol offers the best chance of success.
BSD Unix was the first to make an addition to these two protocols. Called the Transport Layer Interface (TLI), it is now a part of most commercial operating systems. The TLI is another level of abstraction from the Internet protocols and their addressing formats. It provides the ability to implement another protocol on top of any of the existing stacks. For example, a new protocol could be defined that could function on any network where hosts were speaking either TCP or IPX. Unfortunately, TLI programming involves a lot of code-particularly in the area of state control and maintenance. The user must formulate a state machine capable of handling any condition raised from the lower layers. In truth, the two layers available to the nonroot user already handle this, and much work must be duplicated. Given its coding difficulty and the limited number of protocols with which to work, TLI does not appear to be a viable option.