Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does TcpClient write method guarantees the data are delivered to server?

I have a separate thread on both client and server that are reading/writing data to/from a socket.

I am using synchronous TcpClient im (as suggested in documention): https://msdn.microsoft.com/cs-cz/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx

When connection is closed .Read()/.Write() throws an exception. Does it mean that when .Write() method does not throw the data were delivered correctly to the other party or do I need to implement custom ACK logic?

I read documentation for both Socket and TcpClient class and none of them describe this case.

like image 311
Vojtech B Avatar asked Jun 08 '15 07:06

Vojtech B


People also ask

Can TCP client send data to server?

Short answer: Yes, it is possible.

How do you send data from client to server in Socket programming?

Create a socket with the socket() system call. Initialize the socket address structure as per the server and connect the socket to the address of the server using the connect() system call. Receive and send the data using the recv() and send(). Close the connection by calling the close() function.

What is a TcpClient?

The TcpClient class provides simple methods for connecting, sending, and receiving stream data over a network in synchronous blocking mode. In order for TcpClient to connect and exchange data, a TcpListener or Socket created with the TCP ProtocolType must be listening for incoming connection requests.


2 Answers

All that a returning send() call means (or any wrapper you use, like Socket or TcpClient) on a streaming, blocking internet socket is that the bytes are placed in the sending machine's buffer.

MSDN Socket.Send():

A successful completion of the Send method means that the underlying system has had room to buffer your data for a network send.

And:

The successful completion of a send does not indicate that the data was successfully delivered.

For .NET, the underlying implementation is WinSock2, documentation: send():

The successful completion of a send function does not indicate that the data was successfully delivered and received to the recipient. This function only indicates the data was successfully sent.

A call to send() returning does not mean the data was successfully delivered to the other side and read by the consuming application.

When data is not acknowledged in time, or when the other party sends a RST, the Socket (or whichever wrapper) will become in a faulted state, making the next send() or recv() fail.

So in order to answer your question:

Does it mean that when .Write() method does not throw the data were delivered correctly to the other party or do I need to implement custom ACK logic?

No, it doesn't, and yes, you should - if it's important to your application that it knows another party has read that particular message.

This would for example be the case if a server-sent message indicates a state change of some sort on the client, which the client must apply to remain in sync. If the client doesn't acknowledge that message, the server cannot know for certain that the client has an up-to-date state.

In that case you could alter your protocol so that certain messages have a required response which the receiver must return. Do note that implementing an application protocol is surprisingly easy to do wrong. If you're inclined, you could implement having various protocol-dictated message flows using a state machine, for both the server and the client.

Of course there are other solutions to that problem, such as giving each state a unique identifier, which is verified with the server before attempting any operation involving that state, triggering the retry of the earlier failed synchronization.

See also How to check the capacity of a TCP send buffer to ensure data delivery, Finding out if a message over tcp was delivered, C socket: does send wait for recv to end?

like image 82
CodeCaster Avatar answered Oct 07 '22 19:10

CodeCaster


@CodeCaster's answer is correct and highlights the .NET documentation specifying the behavior of .Write(). Here is some complete test code to prove that he is right and the other answers saying things like "TCP guarantees message delivery" are unambiguously wrong:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace TestEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            // Server: Start listening for incoming connections
            const int PORT = 4411;
            var listener = new TcpListener(IPAddress.Any, PORT);
            listener.Start();

            // Client: Connect to listener
            var client = new TcpClient();
            client.Connect(IPAddress.Loopback, PORT);

            // Server: Accept incoming connection from client
            TcpClient server = listener.AcceptTcpClient();

            // Server: Send a message back to client to prove we're connected
            const string msg = "We are now connected";
            NetworkStream serverStream = server.GetStream();
            serverStream.Write(ASCIIEncoding.ASCII.GetBytes(msg), 0, msg.Length);

            // Client: Receive message from server to prove we're connected
            var buffer = new byte[1024];
            NetworkStream clientStream = client.GetStream();
            int n = clientStream.Read(buffer, 0, buffer.Length);
            Console.WriteLine("Received message from server: " + ASCIIEncoding.ASCII.GetString(buffer, 0, n));

            // Client: Close connection and wait a little to make sure we won't ACK any more of server's messages
            Console.WriteLine("Client is closing connection");
            clientStream.Dispose();
            client.Close();
            Thread.Sleep(5000);
            Console.WriteLine("Client has closed his end of the connection");

            // Server: Send a message to client that client could not possibly receive
            serverStream.Write(ASCIIEncoding.ASCII.GetBytes(msg), 0, msg.Length);
            Console.WriteLine(".Write has completed on the server side even though the client will never receive the message.  server.Client.Connected=" + server.Client.Connected);

            // Let the user see the results
            Console.ReadKey();
        }
    }
}

The thing to note is that execution proceeds normally all the way through the program and serverStream has no indication that the second .Write was not successful. This is despite the fact there is no way that second message can ever be delivered to its recipient. For a more detailed look at what's going on, you can replace IPAddress.Loopback with a longer route to your computer (like, have your router route port 4411 to your development computer and use the externally-visible IP address of your modem) and monitor that port in Wireshark. Here's what the output looks like:

Wireshark screen capture of port activity

Port 51380 is a randomly-chosen port representing the client TcpClient in the code above. There are double packets because this setup uses NAT on my router. So, the first SYN packet is my computer -> my external IP. The second SYN packet is my router -> my computer. The first PSH packet is the first serverStream.Write. The second PSH packet is the second serverStream.Write.

One might claim that the client does ACK at the TCP level with the RST packet, but 1) this is irrelevant to the use of TcpClient since that would mean TcpClient is ACKing with a closed connection and 2) consider what happens when the connection is completely disabled in the next paragraph.

If I comment out the lines that dispose the stream and close the client, and instead disconnect from my wireless network during the Thread.Sleep, the console prints the same output and I get this from Wireshark:

Wireshark activity when wireless is disconnected

Basically, .Write returns without Exception even though no PSH packet was even dispatched, let alone had received an ACK.

If I repeat the process above but disable my wireless card instead of just disconnecting, THEN the second .Write throws an Exception.

Bottom line, @CodeCaster's answer is unambiguously correct on all levels and more than one of the other answers here are incorrect.

like image 41
Ben Avatar answered Oct 07 '22 20:10

Ben