简体   繁体   中英

Blocking TCP socket write then immediatelly close - is it a problem?

I have a sender that TCP-connects, sends block of data, and closes the socket. I'd like to write the simplest, but reliable program that does the above. The first thing comes into mind (eg in .NET, although the question relevant to sockets in general):

// assuming LingerOption(false), NoDelay set to whatever
var client = new TcpClient().Connect(server, port);
var stream = client.GetStream();
stream.Write(data, 0, data.Length);
stream.Close();
client.Close();

Now to some questions basing on reading of various MSDN and other materials:

  1. stream.Close() calls Socket.Close(). Socket.Close() is said to close immediatelly, discarding network buffer data which was not send. This is bad. But, Socket.Write documentation says that if the socket is blocking (it is, by default), Socket.Write will block until all the data is sent. So there's no problem, right?
  2. In general, can there be a situation, in which the code above will result in receiver not receiving everything what was sent in Write? (assuming 100% reliable network)

It's is absolutelly OK to call Close just after Socket.Write. Here's what TCP RFC 793 says :

"Closing connections is intended to be a graceful operation in the sense that outstanding SENDs will be transmitted (and retransmitted), as flow control permits, until all have been serviced. Thus, it should be acceptable to make several SEND calls, followed by a CLOSE, and expect all the data to be sent to the destination. "

The confusion, in part, comes from MSDN documentation :

"If you are using a connection-oriented protocol, Send will block until all of the bytes in the buffer are sent "

In reality, blocking Write only copies data to outgoing buffer and returns. It's the network provider responsibility, under RFC 793, to complete the delivery of the data after Socket.Close is called (as long as the connection is not dead, of course).

if the receiver is not reached to Read, you may lose some part of data and this depends on size of buffers used in lower layers.

But If the receiver is blocked on Read, so there is no problem while you provide enough buffer when Reading.

Assuming a reliable network and executing "Read" before "Write" and enough buffer at receiver side, this code is absolutely reliable!

But the solution!:

As i said size of the buffer in receiver side really matters. if the buffer doesn't have enough space, you will lose by this code!

So you have three choices:

  1. Simply allocate a buffer with a enough size.
  2. Use a library or higher protocols to transfer data (for example we have TransmitFile function in Win32 API for transmitting files)
  3. Implement your protocol that before sending data, sends the size of required buffer and then sends actual data.

If you know maximum size of data, then i offer you 1 Else If you don't have enough time and not interested in protocols , then i offer you 2 Else i offer you 3

Good luck ;)

The receiver could send wait or retransmit messages to the sender. This could happen if the receiver's recv buffers are full before the entire block of data is received, or if there are lost packets on the network. So if you quit the client before all the data is exchanged, you lose data.

Take a look at this book for your general solution: UNIX Network Programming Volume One by W. Richard Stevens.

Yes it is UNIX, but it is ultimately dealing with TCP/IP comms and that is at the root of the above. And sorry, it is not really simple, but it's also not really hard either. It's a state machine with more states than your proposal contains.

TcpClient.Close says "The Close method marks the instance as disposed and requests that the associated Socket close the TCP connection. Based on the LingerState property, the TCP connection may stay open for some time after the Close method is called when data remains to be sent. There is no notification provided when the underlying connection has completed closing."

In short, unless you've fiddled with the LingerState, calling Close will not discard the OS buffer of the data - the TCP stack will try its best to deliver the data you've Written.

Be sure to catch exceptions though, any of your Write or the 2 Close calls might throw an exception if things went bad - and make sure you dispose the socket and stream in those cases.

Note also that the Write() call might not block until the data is sent, it'll only block until the data is copied to the TCP stack (and depeding on too many things, TCP might partially send some of that data out on the network before Write returns)

This is sidestepping your original question, but you didn't say what kind of data you were sending. Why did you decide to use a TCP connection in the first place? How often are you writing these blocks of data? If it's very often, I would recommend one of two things:

1) Keep the TCP.Stream open. Otherwise, you could exhaust the TCP stack of available source ports (1025-65535) if you send more than ~64000 blocks in less than 4 minutes. This would get worse if there are other applications open on the source machine. Once this happens your application will hang or error until the first port older than 4 minutes becomes available.

2) Use UDP instead of TCP. There is no guarantee of delivery, but if this is real-time data, then you don't want to use TCP as it could significantly delay real time data.

If you are using TCP, the Client.Close() should send a FIN over the TCP stack, and unless the other host has crashed or the network between them has gone down, should guarantee delivery.

Whether you use TCP or UDP if you are concerned about every block of data arriving at its destination, you should build some application level checking that all blocks arrived in order and uncorrupted...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM