How does the TcpClient write make sure the data is delivered to the server?
I have a separate thread on the client and server that read / write data to / from the socket.
I'm using a synchronous TcpClient im (as suggested in the doc): https://msdn.microsoft.com/cs-cz/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx
When the connection is closed. Read () /. Write () throws an exception. Does this mean that when the .Write () method doesn't throw, the data was delivered correctly to the other side, or do I need to implement custom ACK logic ?
I read the documentation for both the Socket class and the TcpClient and neither of them describe this case.
source to share
All that the call returns send()
(or whatever wrapper you use for example Socket
or TcpClient
) on a streaming, blocking internet socket is that the bytes are put into the buffer of the sending machine.
Successful completion of the send method means that the underlying system has room to buffer your data for sending over the network.
and
Successful completion of the submission does not mean that the data was successfully delivered.
For .NET base implementation is WinSock2, documentation: send () :
Successful completion of the send function does not mean that the data was successfully delivered and received to the recipient. This function only indicates that the data was sent successfully.
A call send()
that returns does not mean that the data was successfully delivered to the other side and read by the consuming application.
If the data is not validated in time, or when the other party sends the RST, Socket
(or any wrapper) will become in a bad state, causing the next send()
or to fail recv()
.
So, to answer your question:
Does this mean that when the .Write () method doesn't throw data, it was delivered correctly for the other side, or do I need to implement custom ACK logic?
No, it isn't, and yes, you do - if it is important for your application to know that the other party has read that particular message.
This would be the case, for example, if a message sent by the server indicates a change of state of some kind on the client that the client must apply to stay in sync. If the client does not acknowledge this message, the server cannot know for sure that the client is up to date.
In this case, you can change your protocol so that certain messages have the required response that the receiver should receive. Note that implementing the application protocol is surprisingly easy to get wrong. If you are inclined, you can implement the different message flows dictated by the protocol using a state machine for both the server and client.
Of course, there are other solutions to this problem, such as giving each state a unique identifier that is verified with the server before attempting to perform any operation on that state, triggering a retry of the previously failed synchronization.
See also How to check the capacity of the TCP send buffer to ensure data delivery , Find out if tcp was delivered , Socket C: is sending to wait for recv to complete?
source to share
@CodeCaster is correct and highlights the .NET documentation defining the behavior of .Write (). Here are some complete test codes to prove that he is right, and the other answers that say "TCP guarantees message delivery" are unequivocally 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 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();
}
}
}
It should be noted that execution is usually through the program, and serverStream does not indicate that the second .Write was not successful. This is despite the fact that there is no way the second message can 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 (for example, have router routing port 4411 to your development computer and use the look and feel of your modem's IP address) and monitor this port in Vireลกarka. This is what the result looks like:
Port 51380 is a randomly selected port that represents the client TcpClient in the code above. There are two packages because this setup is using 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 Stream.Write server. The second PSH package is the second Stream.Write server.
It could be argued that the client is doing an ACK at the TCP layer with the RST packet, but 1) this is not relevant to using the TcpClient as it means the TcpClient is ACKing with a closed connection and 2) considers what happens when the connection is completely disconnected in the next paragraph.
If I comment out the lines that delete the thread and close the client, and instead disconnect from my wireless network during Thread.Sleep, the console prints the same output and I get this from Wireshark:
Basically, .Write returns without exception even if the PSH packet hasn't even been sent, let alone received an ACK.
If I repeat the above process but disable my wireless card instead of just disconnecting, THEN the second .Write throws an exception.
Bottom line, @CodeCaster's answer is unambiguously correct at all levels and more than one of the other answers here is wrong.
source to share
TcpClient uses the TCP protocol, which itself guarantees the delivery of data. If the data is not delivered, you will receive an exception. If no exception is thrown, the data was delivered.
Below is a description of the TCP protocol: http://en.wikipedia.org/wiki/Transmission_Control_Protocol
Each time data is sent, the sending computer waits for an acknowledgment packet to be received, and if it hasn't arrived yet, it will retry sending until it is successful, installed, or a persistent network failure occurs. (for example, disconnecting the cable). In the last two cases, an exception will be thrown
Hence, TCP offers guaranteed data delivery in the sense that you always know if the recipient has received your data or not.
So, to answer your question, you don't need to implement custom ACK logic when using TcpClient as it will be redundant.
source to share
I agree with Denis.
From the documentation (and my experience): This method will block until all bytes are written or an error exception is thrown (like disable). If the method returns, you are guaranteed that the bytes were delivered and read by the other party at the TCP level .
Vojtech - I think you missed the documentation as you need to look at the Stream you are using.
See MSDN NetworkStream.Write method in the notes section:
The Write method blocks until the requested number of bytes has been sent or a SocketException is thrown.
Notes
- Ensuring that the message was correctly read by the listening application is another issue, and the infrastructure cannot guarantee this.
- In asynchronous methods (such as BeginWrite or WriteAsync), this is a different ball game because the method returns immediately and the mechanism for enforcing the completion is different (EndWrite or task completion paired with code).
source to share