Everybody doing some sockets programming is quite familiar with TCP. Most do know UDP and think its an inferior protocol, as it does not ensure that the data bytes sent will arrive at the other site. Thus, UDP imposes less network overhead than TCP and gives the programmer more freedom and more labor by forcing him to deal with the security of the data transport himself.
TCP at a glanceLet's first take a look at some TCP details. Before any data can be exchanged with TCP, a socket needs to be opened. By this a point-to-point connection is established that provides a bidirectional data path between the partners. While establishing a socket needs a client and a server site (the server offering potential sockets, the client using one of them), once the initialization done, the data transport is done identically in both directions and the two ways are completely independent. Delphi users sometimes are not aware of this, as Indy implements the receiving interface differently with IdTCPClient and IdTCPServer and thus, using Indy, having the client answer to a dialog initiated from the server site is a bit tricky (see article Screen Thief).
Regarding the data transport, we need to be aware, that TCP works on data streams, and not on data blocks. The secure TCP frame transport and the packet structure of the underlying IP protocol is completely hidden. The transport mechanism even will split and concatenate blocks on the fly, trying to create as big IP packets as possible, to reduce the overhead, and using one IP packet for each TCP frame to facilitate the correction of lost packets.
While with Indy you usually use functions like TIdTCPClient.Write or TIdTCPClient.WriteBuffer that send a "block" of data, you can't be sure that the receiver will get this data in the same block structure. It can get it in more than one block or it can get a block that contains this data preceded or followed by other data. So the software needs to implement a scheme that finds the start of the logical data blocks that are to be handled. In most cases this is easy, as you can be sure that no byte is lost as long as the socket lives, and thus the structure of the data is intact and can be decoded without any afterthought. Anyway, for a socket connection that stays established for a long time, it's a good idea to use some kind of unique signature to mark the start of each block. Of course, if the signature might be part of the data to be transferred, some tranparentizing scheme needs to be implemented.
UDP? UDP!In contrast, UDP works on data blocks. A data block can be up to 64 K bytes. A UDP block is considered transferred, if all IP packets, it consists of, are received. If one of them is lost, the complete UDP block is discarded.
Using Indy, you need to set the buffer size of the UDP components according to the longest block you want to use. The default size is 8 K. But even if setting the buffer size to 64K, I was not able to transfer bigger blocks than 16K. I suppose this is a limitation of the Windows version I used.
Regarding the UDP standard definition, there is no such thing like a socket that defines a connection between two sites, but each block transport defines it's own target. Thus the definition of client and server is different from how it is used with TCP. A UDP server just provides a port where any device on the net can send UDP data blocks to. There are UDP senders and UDP receivers. Indy obfuscates this by defining TIdUDPClient and TIdUDPServer components that both can write UDP blocks, while supposedly only TIdUDPServer can provide a port, network devices can write to. Moreover TIdUDPClient has Host and Port properties that define the target, but nothing prevents them from being changed for each packet before sending it. I was not able to use TidUDPServer to send a data block to a dedicated address, while the Broadcast property is set. So TIdUDPClient and TIdUDPServer components are active in the example program and the "server" is used only for reading while the "client" is only used for writing.
A consequence of the UDP transport algorithm is, that you can do UDP broadcasts: messages that go to all UDP receivers on the network that opened a port to receive them. (Beware that most routers block broadcasts, and thus their reach is limited to an appropriate network segment.)
Regarding the data structure, UDP preserves the data block limits during the transport. That is why TIdUDPServer provides an event that allows dedicated access to each block received. Each block can come from a different network device, so the event provides the Binding that holds the correct specification of the sender of this data block (even if more blocks already arrived before the user program was able to start handling this event)
Example Chat program (with source) shows how UDP can be used to find partners with unknown IP addresses.