Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a successful send() "atomic"?

Does a successful call to send() with the number returned equal to the amount specified in the size parameter guarantee that no "partial sends" will occur?

Or is there some way that the OS might be interrupted while servicing the system call, send part of the data, wait for a possibly long time, then send the rest and return without notifying me with a smaller return value?

I'm not talking about a case where there is not enough room in the kernel buffer; I realize that I would then get a smaller return value and have to try again.

Update: Based on the answers so far, my question could be rephrased as follows:

Is there any way for packets/data to be sent over the wire before the call to send() returns?

like image 387
lxgr Avatar asked Nov 07 '11 18:11

lxgr


People also ask

Is socket send atomic?

For TCP sockets, if the value you specify for the len parameter is less than or equal to the send buffer size of the socket, the send process will be atomic; that is, either all of the data will be sent or none of it will be sent. If all of the data is sent, the return code is set to the value of the len parameter.

Is TCP Atomic?

No. TCP is a byte-stream protocol. No messages, no datagram-like behaviour.

What will indicate with a successful return from write to a TCP socket?

Returned Value If successful, write() returns the number of bytes actually written, less than or equal to N . If unsuccessful, it returns the value -1 and sets errno to one of the following: A value of 0 or greater indicates the number of bytes sent.


2 Answers

Does a successful call to send() with the number returned equal to the amount specified in >the size parameter guarantee that no "partial sends" will occur?

No, it's possible that parts of your data gets passed over the wire, and another part only goes as far as being copied into the internal buffers of the local TCP stack. send() will return the no. of bytes passed to the local TCP stack, not the no. of bytes that gets passed onto the wire (and even if the data reaches the wire, it might not reach the peer).

Or is there some way that the OS might be interrupted while servicing the system call, send part of the data, wait for a possibly long time, then send the rest and return without notifying me with a smaller return value?

As send() only returns the no. of bytes passed into the local TCP stack, not whether send() actually sends anything, you can't really distinguish these two cases anyway. But yes, it's possibly only some data makes it over the wire. Even if there's enough space in the local buffer, the peer might not have enough space. If you send 2 bytes, but the peer only has room for 1 more byte, 1 byte might be sent, the other will reside in the local tcp stack until the peer has enough room again.

(That's an extreme example, most TCP stacks protects against sending such small segments of data at a time, but the same applies if you try to send 4k of data but the peer only have room for 3k).

I'm not talking about a case where there is not enough room in the kernel buffer; I realize that I would then get a smaller return value and have to try again

That will only happen if your socket is non-blocking. If it's blocking and the local buffers are full, send() will wait until there's room in the local buffers again (or, it might return a short count if parts of the data was delivered, but an error occured in the mean time.)

Edit to answer:

Is there any way for packets/data to be sent over the wire before the call to send() returns?

Yes. That might happen for many reasons. e.g.

  • The local buffers gets filled up by that recent send() call, and you use blocking I/O.
  • The TCP stack sends your data over the wire but decides to schedule other processes to run before that sending process returns from send().
like image 77
nos Avatar answered Nov 04 '22 19:11

nos


Though this depends on the protocol you are using, the general question is no.

For TCP the data gets buffered inside the kernel and then sent out at the discretion of the TCP packetization algorithm, which is pretty hairy - it keeps multiple timers, minds path MTU trying to avoid IP fragmentation.

For UDP you can only assume this kind of "atomicity" if your datagram does not exceed link frame size (usual value is 1472 = 1500 of ethernet frame - 20 bytes of IP header - 8 bytes of UDP header). Otherwise your sending host will have to IP-fragment the datagram.

Then intermediate routers can still IP-fragment the passing packet if their outgoing link MTU is less then the packet size.

like image 22
Nikolai Fetissov Avatar answered Nov 04 '22 20:11

Nikolai Fetissov