2009-06-13

Using Socket as a Connected Socket

(This post is part of the TCP/IP .NET Sockets FAQ)

A connected socket is one which has a connection to the remote side. When a client socket connects to a listening server socket, the result is two connected sockets: the client socket becomes connected, and the listening server creates a new socket that is connected. For more details about establishing or listening for socket connections, see Using Socket as a Client Socket and Using Socket as a Server (Listening) Socket.

Important note: A socket only believes it is currently connected; it can never know for sure. It is possible for one side of a connection to realize it is no longer connected, while the other side continues believing it is connected. This is called the "half-open problem", and is covered in detail in Detection of Half-Open (Dropped) Connections.

There are two primary operations performed on connected sockets: Read and Write. Connected sockets may also Disconnect or Close the connection; these operations will be covered in more detail in a future FAQ entry.

Writing

A socket may be written to at any time. A Write operation places bytes into the outgoing stream. If using asynchronous Write operations, multiple Write operations may be started, and the bytes will be placed into the outgoing stream in the correct order.

Important note: The completion of a Write operation does not mean that the remote side has received the data.

The Write operation completes when the local OS has copied the entire write buffer, even though those bytes may not have been sent out on the network yet. Beginning TCP programmers often balk at this, because they think that they must know if data has been received by the remote side. This reaction is called "send anxiety", and will be covered in a future FAQ entry.

Write operations may not complete immediately. TCP allows one side to inform the other side of how much buffer space it has; therefore, if the remote application is reading the bytes slowly, then the socket's send buffer may fill up, and the socket may not send the outgoing bytes immediately. In fact, it is possible to end up in a deadlock situation if both sides send lots of data but read only a little. This is one reason why seasoned socket programmers almost always use asynchronous Write operations instead of synchronous.

A Write operation may (immediately) fail; this is the most common way to detect dropped connections. When a Write operation fails, the application should assume that the connection is no longer viable; see Error Handling for details.

Error Detection

It is possible that the Write operation may fail after it completes. TCP has a built-in retry mechanism, so the Write will only fail if it is quite sure the connection is no longer viable. In this situation, there is not a way for the OS to signal the application, so the it places the socket into an error state. This causes future socket operations to fail.

Most TCP protocols include a notion of a "keepalive message" which is written to the socket periodically (at least if there has been no other socket activity for some time). This enables the application to detect socket errors from "successful" Write operations that later failed. It also enables the application to detect lost connections, preventing the "half-open problem". Keepalive messages are discussed in more detail in Detection of Half-Open (Dropped) Connections.

Reading

As long as the socket is connected, the OS is constantly reading on behalf of the application (unless the socket's receive buffer has been disabled). The incoming bytes are stored in the socket's receive buffer and held there until the application starts a Read operation. It is possible to start more than one asynchronous Read operation at a time, but this is strongly discouraged because the operations may complete out of order.

When an application performs a Read operation, it is requesting to read N bytes from a socket. The OS will not wait until all N bytes arrive; rather, it may complete the Read operation when it has at least one byte to return to the application. When an application requests to Read N bytes, it actually receives at least one byte and at most N bytes. This clears out the OS receive buffers faster and gets the data to the application sooner, but this also means that the application must deal with "partial receives". Common ways of handling this are covered in Message Framing.

It is important for an application to Read from the connection on a regular basis, to prevent the deadlock situation described above under "Writing". For this reason, experienced socket programmers usually have a single asynchronous Read operation always running on a connected socket. Whenever the Read operation completes, another asynchronous Read operation is started.

Another advantage of reading constantly is that misbehaving applications are immediately detected. Most protocols have certain times when it would be an error for the remote side to send data. If the application does not constantly Read, then any data arriving at that time would be treated as data arriving at a later time. It is easier to debug misbehaving applications if the incoming data is read and logged at the time it arrives at the socket.

Reading Zero Bytes

Many stream-oriented objects (including sockets) will signal the end of the stream by returning 0 bytes in response to a Read operation. This means that the remote side of the connection has gracefully closed the connection, and the socket should be closed.

The zero-length read must be treated as a special case; if it is not, the receiving code usually enters an infinite loop attempting to read more data. A zero-length read is not an error condition; it merely means that the socket has been disconnected.

Important note: Most of the MSDN .NET socket examples do not handle this correctly! They will enter an infinite loop if the socket is closed by the remote side.

Disconnecting

Either side of a socket connection may initiate a Disconnect operation or Close the socket. Once one side of the connection starts disconnecting, the socket is no longer fully connected. It is possible for it to be partially connected for some time; this state is called "half-closed". Disconnecting socket connections (including the half-closed state) will be covered in a future FAQ entry.

(This post is part of the TCP/IP .NET Sockets FAQ)

6 comments:

  1. When i use the following code in .NET 1.1 in get the following error "A socket operation was attempted to an unreachable host" below is the code...

    main_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    main_ipEndPoint = new IPEndPoint(Dns.GetHostByName(server).AddressList[0], this.port);

    main_sock.Connect(main_ipEndPoint);

    Can u please help me in this regard.

    Thanks

    ReplyDelete
  2. @Anonymous:

    http://nitoprograms.blogspot.com/2009/05/getting-local-ip-address.html

    ReplyDelete
  3. 안녕하세요?
    Thanks for this information. especially "Reading Zero Bytes" was of great help to me.
    If you can read Korean, I could be able to write many thanks.
    Have a nice day.

    ReplyDelete
  4. You have covered the topic well, congratulations.

    ReplyDelete
  5. Your documentation is very helpful. Thank you.

    ReplyDelete
  6. You are right!

    I should have read this article esp. "Reading Zero Bytes" part!! I took so much time to figure it out by myself.

    MSDN had no examples over zero bytes case, and i found out just take it as exception, infinity loop won't be happened again :)

    ReplyDelete