DWise1's Sockets Programming Pages: Basic TCP/IP Theory

DWise1's Sockets Programming Pages

Basic TCP/IP Theory


HARD HAT AREA

WATCH YOUR STEP

Contents


Introduction

In order to do sockets programming, you need to have a basic knowledge of what TCP/IP is and how it works. This page is intended to introduce you to that information.


What is TCP/IP?

Basically, TCP/IP is one particular collection of networking protocols, one of many. What makes TCP/IP so special is that it is the protocol suite that the Internet is based on and so support for it has become almost universal.

TCP/IP stands for "Transmission Control Protocol/Internet Protocol":


TCP/IP and Packets

The unit of transfer in TCP/IP is the packet. A packet is an individual message, which is sometimes compared to a snail-mail letter. Each packet has a header which contains a source address, a destination address, and additional information needed by the system. It also has a data block, which is most often the actual message being sent. The format of the packets' headers are well-defined, whereas the format of the data block depends entirely upon the application-level protocol that is using it.

Within the framework of TCP/IP, the TCP portion deals with the creation and handling of the packets themselves while the IP portion deals with getting those packets to their destination.

Sockets programming deals with the creation and handling of these packets. You normally never need to deal with the actual format of the packet; the data in the packet is the only thing whose format you have any control over. In general, you provide the destination address, the data block, and which protocol (tcp or udp -- see below) and the system provides the rest.


The TCP/IP Layers Model

Application Layer
Transport Layer
IP Layer
Data Link Layer
Sorry to do this to you, but just about every book on the subject presents either this layer model or the 7-layer OSI model. Still, it is useful for keeping a few concepts straight. My treatment of it here is far from complete.

The basic function of each layer is:

This layer model helps to keep a few important concepts in mind:

  1. The overall networking task is split up among the different layers, with each layer performing a specific part of that task.
  2. The data flowing out of the source computer heads down through the layers from application to data link and the data flowing into destination computer heads up through the layers from data link to application.
  3. The connection effectively works as if the corresponding layers of the connected computers were communicating directly with each other.
  4. Each layer has its own protocols that govern how it communicates with its opposite number on the other computer.
  5. Each layer does its own job without having to know exactly how the layers above and below do their jobs.

Sockets operate primarily within the Transport Layer.
Sockets programming is the interface between the Application Layer and the Transport Layer.


tcp and udp Protocols

TCP itself is divided into two different protocols: tcp and udp. The difference between tcp and udp is in the type of connection that they form between the computers and the consequences that has for the transfer of data: There are a number of factors to consider in choosing between tcp and udp, such as: Many sources discuss these factors in greater detail and clarity, such as Jon Snader's Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs. In general, tcp seems to be used more often than udp, but there are applications for which udp is the clear choice.


IMPORTANT NOTE:

In a given sockets connection, both the source and the destination must use the same protocol. You either use tcp or you use udp; you cannot use both at the same time. It is a common mistake to try to connect to a tcp server with a udp client or vice versa. It just won't work.

Of course, if the application uses multiple sockets (eg, FTP uses two connections; one for control messages and the other for data transfer), then some of the sockets can use tcp while the others use udp. But the rule still applies that an individual socket must be purely tcp or udp.


The TCP Connection

For the TCP protocol to manage the connection requires a lot of messages and handshaking to coordinate everything, all of which creates a lot of overhead that makes the much simpler (and less reliable) UDP protocol more attractive for some applications. All I want to do here is to introduce a few of the basic ideas that have a more direct bearing on sockets programming. If you want to get into the details, it's all covered in the 84 pages of RFC 793, "Transmission Control Protocol" (01 Sep 1981), and has been explained in several books and on countless web sites.

First, TCP packets are commonly refered to as "segments", I think mainly because a larger "packet" gets split up, or "segmented", into several smaller packets, transmitted, and reassembled on the other end (just how TCP does that involves a lot of the handshaking in the overhead). At the beginning of every TCP segment is a 24-byte header that contains all the information TCP needs to manage the connection (full description starts in Section 3.1 of RFC 793). This includes six flags that are used in opening and closing the connection and in data transfer:

Almost all the TCP operations are described in terms of what these six flags are set to. In most descriptions, the sending of a TCP segment with a particular flag set is described as "sending a <flag name>"; eg, sending a TCP segment with the SYN flag set is called "sending a SYN".


Connecting (The "Triple Handshake")

In almost all descriptions of TCP, you will read mention of the "Triple Handshake". It's called that because it takes three TCP segments to establish the connection. For example, if Host1 wants to establish a connection with Host2:
  1. Host1 sends a SYN to Host2 with an initial sequence number, x.
  2. Host2 responds with a segment in which both ACK and SYN are set with an acknowledgement number of x+1 (ie, next sequential) and with its own initial sequence number, y.
  3. Host1 acknowledges Host2's SYN by sending an ACK with an acknowledgement number of y+1.
In terms of sockets programming, this all happens when Host1 calls the
connect() function with Host2's address.

There are a number of error conditions that can arise and that must be dealt with, which would include the need for a reset. These are described and discussed in Section 3.4, "Establishing a connection", of RFC 793.


Disconnecting (Shutting Down Gracefully)

While there are many possible ways to land an aircraft, we only want to use the kinds of landings that we can walk away from. Similarly, while there are many ways of ending a TCP connection, we want to learn to use the ones that allow our applications to disconnect cleanly and without loss of data. In other words, we want to use a graceful shutdown instead of simply crashing the connection.

The definition of a "graceful shutdown" is tied to a primary purpose of TCP, which is to ensure the arrival of all the data sent. When an application sends data, the system copies the data to a transmit buffer and reports success; the data has not actually been sent yet, but rather it's ready for TCP to deliver it. By the definition of a "graceful shutdown", we need to ensure that all the data that has been buffered does get delivered; if we fail in that, then the shutdown was not graceful.

The general strategy for achieving a graceful shutdown is to have one host indicate that it will send no more data (i.e., does a shutdown), whereupon its peer sends the last of its data and then does its own shutdown and closes its socket. After receiving the last of the data and its peer's shutdown notification, the first host closes its own socket.

This is an important idea to understand and there is a tendency for beginners to write rude applications that just "slam the connection shut". I think we need to get straight from the horse's mouth, RFC 793, Transmission Control Protocol, pp 37-38, what a "graceful shutdown" requires of us (my emphasis):

3.5. Closing a Connection
CLOSE is an operation meaning "I have no more data to send." The notion of closing a full-duplex connection is subject to ambiguous interpretation, of course, since it may not be obvious how to treat the receiving side of the connection. We have chosen to treat CLOSE in a simplex fashion. The user who CLOSEs may continue to RECEIVE until he is told that the other side has CLOSED also. Thus, a program could initiate several SENDs followed by a CLOSE, and then continue to RECEIVE until signaled that a RECEIVE failed because the other side has CLOSED. We assume that the TCP will signal a user, even if no RECEIVEs are outstanding, that the other side has closed, so the user can terminate his side gracefully. A TCP will reliably deliver all buffers SENT before the connection was CLOSED so a user who expects no data in return need only wait to hear the connection was CLOSED successfully to know that all his data was received at the destination TCP. Users must keep reading connections they close for sending until the TCP says no more data.

There are essentially three cases:

1) The user initiates by telling the TCP to CLOSE the connection

2) The remote TCP initiates by sending a FIN control signal

3) Both users CLOSE simultaneously

Case 1: Local user initiates the close
In this case, a FIN segment can be constructed and placed on the outgoing segment queue. No further SENDs from the user will be accepted by the TCP, and it enters the FIN-WAIT-1 state. RECEIVEs are allowed in this state. All segments preceding and including FIN will be retransmitted until acknowledged. When the other TCP has both acknowledged the FIN and sent a FIN of its own, the first TCP can ACK this FIN. Note that a TCP receiving a FIN will ACK but not send its own FIN until its user has CLOSED the connection also.
Case 2: TCP receives a FIN from the network
If an unsolicited FIN arrives from the network, the receiving TCP can ACK it and tell the user that the connection is closing. The user will respond with a CLOSE, upon which the TCP can send a FIN to the other TCP after sending any remaining data. The TCP then waits until its own FIN is acknowledged whereupon it deletes the connection. If an ACK is not forthcoming, after the user timeout the connection is aborted and the user is told.
Case 3: both users close simultaneously
A simultaneous CLOSE by users at both ends of a connection causes FIN segments to be exchanged. When all segments preceding the FINs have been processed and acknowledged, each TCP can ACK the FIN it has received. Both will, upon receiving these ACKs, delete the connection.
[Graphs follow on page 39 depicting message exchanges between hosts for these three cases]

In terms of the sockets programming needed to support a graceful shutdown:

  1. The host initiating the shutdown performs a shutdown(1) call. This indicates to its own protocol stack that it does not intend to send any more data on this socket. If the host does try to send more data on that socket, it will be rejected with an error. However, the host can still receive data on that socket and indeed must be listening for incoming data.

  2. The fact that its peer has shut down the connection is reported via the return value of the recv() function.
    recv() returns either of these three int values:
    1. > 0 -- the number of bytes read into the buffer
    2. == -1 -- an error has occurred
    3. == 0 -- the peer has shut down the connection; there is no more data to be received

  3. The host closes the socket itself with the close() call; Winsock requires use of the special function, close_socket().
    The typical manner in which to rudely "slam the connection shut" is by calling close() / close_socket() without first having called shutdown(1) and waited to detect (via recv() returning a zero) the peer's own shutdown.

More specifically, the general "graceful shutdown" procedure between Host1 and Host2 should be:

  1. Host1 sends the last of his data and calls shutdown(1) to initiate the shutdown. Host1 then start looping on calls to recv() in order to receive the rest of the data (if needed) and to look for notification of Host2's own shutdown.

  2. Host2 receives the last of Host1's data and then receives a return value of zero from recv(). Recognizing this as a shutdown notification, Host2 sends the last if its data (if any) to Host1 and then calls shutdown(1) as well.

  3. Host1 receives a return value of zero from recv(). Recognizing this as Host2's shutdown notification, Host1 closes its socket.

  4. Having received an ACK to its FIN, Host2 closes its socket.

Ports and Sockets

First, we need a few definitions:


In TCP/IP, each address consists of:

  1. An IP address. This gets you to a particular interface on a particular computer. Every host on a network and every host on the Internet must have its own unique IP address; there must not be any duplicates. Read the next section for a description of IP Addressing.

  2. A protocol. In our cases, that would be either tcp or udp.

  3. A port number. Conceptually, this is the point in the computer's interface where the actual connection is made between hosts. There are 65,536 ports for tcp and another 65,536 ports for udp, though the operating system will limit how many ports can be open at the same time.
In every sockets application, you designate which protocol you are using and which local port (though you can allow the OS to assign a port for you) -- please note that in most cases your own IP address is already known, though you may need to specify one if you are multi-homed. Then if you are making a connection with another host, you designate the IP address and port you want to connect to, or, if you are acting as a server, then you get the client's IP address and port when it connects to you.


So now we can answer that question that you must be asking: why is it called "sockets"? A socket is defined as a software construct for handling a port. You can think of the ports as the specific points at which the computers connect to each other and a "socket" as the software interface to a port. Or you could think of a "socket" as where your application plugs in to form the connection. Actually, both ports and sockets are software abstractions, since there aren't really 131,072 tiny connectors in there, even though it helps us to visualize that there are.

Each socket is uniquely identified by a pair of addresses: the local address and the peer's address. This is important to remember, because we must be careful not to associate a socket too closely with a local port. There is a cardinal rule that a given socket can only be used for one connection at a time. But if we run a server, say a web server on TCP port 80, and ten clients connect to us, then we will see that our local TCP port 80 is involved in each of those ten connections. That appears to violate that cardinal rule. However, what is really happening is that each of those are different sockets, because each of them has a different peer address. We are not using the same socket ten times over, but rather we are using ten different sockets. There is no actual conflict nor violation of the rule.


Refering back to the protocol layers, the IP layer recognizes its own IP address in the incoming packet header and passes the packet up to the Transport layer, which is where the ports are. There, the protocol and port in the destination address are matched to the peer socket that is bound to that port -- if any -- and the packet is read by the associated application. Of course, if the peer has not bound a socket to that port, then you will not be able to connect to it.

In TCP/IP, there are two sets of ports: the tcp ports and the udp ports, which are used by the tcp protocol and by the udp protocol respectively. There are 65,536 tcp ports and 65,536 udp ports, which are all divided into three ranges:

Port Range Used For:
The Well Known Ports 0 through 1023 reserved for common services like telnet, ftp, sendmail
The Registered Ports 1024 through 49151 registered by companies and organizations
The Dynamic and/or Private Ports 49152 through 65535 available for the rest of us to use
This solves the problem that you should have been wondering about: how does a client know which port to connect to on the server? Obviously, he needs to know ahead of time which port is being used. Well, in the case of a well-known port or a registered port, like telnet(23) or http(80) or pcANYWHERE(5631), then he already knows because it has been determined before-hand. Otherwise, the client has to have been informed of the server's port through some other arrangement.

In sockets programming, we attach our socket to a port (called "binding the socket") and send or receive packets to or from another port somewhere else, usually on another computer. The binding of a port to one address and connecting it to another address is accomplished through functions in the sockets API.

A list of the assigned port numbers is posted on IANA's site.


IP Addressing

There are two forms of IP addressing: IP Version 4 (IPv4) and IP Version 6 (IPv6). IPv6 is the future replacement for IPv4, but IPv4 is still the most commonly used form and the one which I will cover. You can read about IPv6 elsewhere.

In the protocol layers, the IP layer is responsible for handling IP addresses and for recognizing its own IP address in the incoming packet header. In TCP/IP, every device on a network, including on the Internet, has a unique IP address. This is true of computers, network printers, and routers. Each network device has at least one network interface (e.g., a NIC) and some have more than one (e.g., a router, which by definition is connected to at least two networks). Each network interface has a unique IP address assigned to it.

In IPv4, an IP address is given as a series of four numbers separated by periods in what is called "dotted-decimal" format. Each of these numbers falls in the range of 0 to 255 and is called an "octet", because it is eight bits long. The reason that they are not called "bytes" is that for most of the history of electronic computers (about the mid-1940's to the mid-to-late 1970's), every kind of computer had its own definition of how long a byte was. On the other hand, the term "octet" specifically means 8 bits, so there is no ambiguity.

A device on a network is called a host. The first bits in an IP address are the network bits, which uniquely identify which network the host is on. The remaining bits uniquely identify the host. Exactly where the network bits end and the host bits begin depends on a number of things and is too involved to get into right now. Suffice to say that for two hosts to talk to each other, they either have to be on the same network or there has to be a router or routers that eventually connect their networks. This is one of the "gotcha's" that most beginners fall victim to.

If you need to set up your own local network and select your own IP addresses, then you should also read my basic instructions for selecting an IP address for a host. In addition, there are a lot of really good explanations on the Internet. There's no such thing as learning too much.


Byte Order

A mundane-sounding topic, yet this is a prime source of problems for the beginner. I know, because I've fallen for it, too.

Most data values in a computer are contained in strings of bytes. However, the order in which those strings of bytes are stored can vary from computer to computer. Terms like "big-endian" and "little-endian" are used to describe how computers organize multi-byte data. Since we are going to be exchanging binary data directly between a wide variety of computers, we need to standardize the format of the data.

Therefore, all multi-byte values used by the tcp and udp protocols must be in one specific order, called "network order". The order that your computer keeps these values in is called "host order". If your host order is different from network order and you fail to compensate for it, then your sockets will not work. Period. End of story. That's all she wrote.

The good news is that you do not need to know what order your local host uses. The sockets API provides a number of conversion functions, including ones that will convert host-order values into network order and vice versa. The functions for converting byte order are:

  • unsigned short htons(unsigned short hostshort) --
  • "host to network, short", for converting a two-byte integer from host order to network order
  • unsigned long htonl(unsigned long hostlong) --
  • "host to network, long", for converting a four-byte integer from host order to network order
  • unsigned short ntohs(unsigned short netshort) --
  • "network to host, short", for converting a two-byte integer from network order to host order
  • unsigned long ntohl(unsigned long netlong) --
  • "network to host, long", for converting a four-byte integer from network order to host order

    The recommended procedure is that you keep the values in host order and convert them when you move them into and out of socket structures, such as when dealing with port numbers. They should also be useful when constructing or parsing the data block.

    As I learned years ago working construction: let your tools do the work for you.


    Return to Top of Page
    Return to DWise1's Sockets Programming Page
    Return to DWise1's Programming Page

    Contact me.


    Share and enjoy!

    First uploaded on 2003 July 26.
    Updated 2011 July 18.