I am writing this server:

var tcp = new TcpListener(); tcp.Start(IP, port); //Далее вообще-то следует код, который по идее должен Accept()ить клиента, //но, допустим, я допускаю баг в коде сервера, в результате у меня на сервере происходит только запуск сервера //то есть после Start() ничего нет 

Meanwhile, a client like this:

 var tcp = new TcpClient(); try { tcp.Connect(ip, port); } catch (Exception ex) // ----- Check #1 { log("Can't connect"); return; } log("Connected"); //Sending first HeartBeat var buf = Encoding.UTF8.GetBytes("heartbeat"); tcp.GetStream().Write(buf, 0, buf.Length); //Loop for reading response for HeartBeat and for another messages while (true) { var buf = new byte[10]; int nReaden = -1; try { nReaden = tcp.GetStream().Read(buf, 0, buf.Length); } catch (Exception ex) // ----- Check #2 { log("Can't read because connection closed by server / Ethernet cable unplugged / Wi-Fi disconnected / etc"); return; } log(nReaden + ": " + Encoding.UTF8.GetString(buf)); //TODO Sending next HeartBeat } 

I try it at work.
I start the server.
I start the client - I see in the "Connected" log and the fact that Check # 1 and Check # 2 do not work, in any case, I wait a few minutes and Check # 2 does not work

I look at Wireshark on the client - and I see that everything works as if accept () was on the server.
I.e:
- when client.Connect server returns ACK, respectively, on client Check # 1 does not work
- when client.Write server also returns ACK, respectively, on client Check # 2 does not work either
- the client thinks that everything is in order.

But there is no accept () on the server, so in fact the server does not receive the message.

Of course, this fictional situation with a bug on the server is more an academic issue than a practical one.

1) But still interesting - why the creators of TCP did just that, because IMHO is the wrong decision?
They could make the ACK about a successful SYN come to the client only after the server has made a successful accept () of the given client.
Why not done?

2) Is it possible to somehow fix the "problem" by regular TCP means? (that is, without screwing a custom timeout timer to the client, which will start after Write () and wait for Read () for NNNN ms, and if not, then throw Exception) ?
It is the way with its timeout that seems to me the most reliable in all respects (including psychological reliability), but it is confusing and curved, it can hardly be recommended.

  • one
    But there is no accept () on the server, so in fact the server does not receive the message. - it depends on accept semantics, until I have seen a promise anywhere that accept is participating in a three-way handshake. It can simply transfer an existing connection. - etki
  • @Etki Transferring from the system kernel to my application? - KG
  • Something like that. To give you an interface for communication without making any network actions. - etki
  • @Etki So I understood, but let us return to the question - why is this done? Why it designed so? Do you have something to say about this? Imagine if you were creating your own transport protocol for internal use. At the same time, unlike the creators of TCP, you have experience proving that TCP has specific imperfections that it is better to refuse in its protocol. And here it is - one of them. Do you agree? If not, can you argue? - KG

1 answer 1

From the point of view of the TCP implementation in the OS, you are ready to accept connections (to accept ) at the moment you called listen , which is what happens to you "behind the scenes" in tcp.Start(IP, port); .

And the call to accept only returns to the application program a new socket associated with the connection already made in the OS.

In this regard, we can consider accept behavior in the same way as read , with the difference that read returns data sent by the client, and accept "service information" (a new connection to a given port).