views:

841

answers:

5

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 (e.g. 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)
A: 

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 ;)

Isaac
So, assuming receiver starts to Read immediatelly after Accept and reads until socket the is closed - my code is absolutelly reliable, no matter what? Thanks.
Borka
@Borka: Assuming a reliable network and executing "Read" before "Write" and enough buffer at receiver side, this code is absolutely reliable.
Isaac
@Isaac - fine but in reality you simply cannot expect those assumptions to be correct.
Eric M
ie correct in general.
Eric M
@hoohoo - Reliable network is a requirement for transferring. although TCP protocol fixes some errors, but a really unreliable network is not a network!I discussed about the size of buffer and i hope it is clear. But about Executing "Read" before "Write", this condition always met if you have a dedicated receiver. But when we have a shared receiver (that called a "Server") we have to "negotiate" to it before sending a large data
Isaac
@Isaac: "unreliable network is not a network" - that is sophism.
Eric M
@hoohoo - oh. come on! Here we are not discussing about philosophy!Let me tell it better! "When a network becomes unreliable, it is not called a network anymore"
Isaac
@Isaac - it is still a network, just an unreliable one. IP networks can and do drop packets, and retransmit messages are sent. This is why you cannot just send and close as you described in yr original post. Your position that it is not a network anymore is foolish. It leads to designs that do not take account of this reality. Thus sophism. Not philosophy, just valid or invalid design. I'd say the same to proposal that some functions' return values need not be checked "because they cannot fail".
Eric M
@hoohoo - It depends on the viewpoint and where we are. When we are using TCPSockets, we are at the Session Layer and we HAVE a reliable network OR we don't have any network! (the cable is cut or too noisy). Because in Session Layer, all we should do is just sending data and closing the connection. Lower layers will manage the random noises and fix errors and we should not worry about it. But if the network is too noisy, we cannot do anything to solve it! we should correct the network, not trying to write a code to fight to this errors in Session Layer! that's ridiculous!!
Isaac
@hoohoo, @Isaac, guys, network reliability is not an issue. I have my app-layer-reliability on top of the above code snippet. I just want to make sure that the socket-dealing code below the app layer doesn't do something completely wrong, often.
Borka
it won't. If you just "Read" before "Write" and supply enough buffer in receiver.
Isaac
@borka, OK, I guess this is not the place for our debate.@Isaac, maybe we should take it to usenet :-)
Eric M
+1  A: 

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.

Eric M
Indded, a very good book you're talking about. I'll do some deep reaqding and will post an update above. Thanks.
Borka
A: 

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)

leeeroy
A: 

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...

Scott Lundberg
+1  A: 

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).

Borka