When Will Send() Return Less Than The Length Argument

When will send() return less than the length argument?

In general, if the transmit buffer contains some space, but not enough for the entire send request then it will send as much as it can and then return the amount actually added to the buffer -- a short write.

Now you could argue that it would make more sense to block (on a blocking socket), but the reason it doesn't is historical -- TCP is based on UNIX pipes, and that's the way UNIX pipes worked. The main reason is that it makes corner cases (in the kernel) easier -- you don't have to worry about blocking a system call in the middle of doing something; it either does something and returns immediately or does nothing and blocks until some event (at which point the kernel retries it from scratch). You don't have to worry about what happens if someone tries to write more than the maximum buffer size in a single write (which might otherwise cause a deadlock).

Can send() on a TCP socket return =0 and length?

Your point 2 is over-simplified. The normal condition under which send returns a value greater than zero but less than length (note that, as others have said, it can never return zero except possibly when the length argument is zero) is when the message is sufficiently long to cause blocking, and an interrupting signal arrives after some content has already been sent. In this case, send cannot fail with EINTR (because this would prevent the application from knowing it had already successfully sent some data) and it cannot re-block (since the signal is interrupting, and the whole point of that is to get out of blocking), so it has to return the number of bytes already sent, which is less than the total length requested.

Why is it assumed that send may return with less than requested data transmitted on a blocking socket?

The thing that is missing in above description is, in Unix, system calls might get interrupted with signals. That's exactly the reason blocking send(2) might return a short count.

What factors will lead to linux send() function return 0?

What factors will cause that other than massive data transfers where the other end is not keeping up.

That's not one of the factors at all.

I know it usually causes by massive data transfers where the other end is not keeping up.

No it isn't. That only happens in non-blocking mode, and it causes send() to return -1 with 'errno' set to EAGAIN/EWOULDBLOCK.

You are mistaken about this.

send() will return zero if and only if you provide a zero length.

That's what the man page says, and has said since about 1983, and it is mandated by the Posix and Winsock specifications.

send() function in C how to handle if not all bytes are sent?

Yes, you have to advance the buffer pointer and repeat the send call until either the entire buffer is consumed or you get a hard error. The standard idiom looks something like this:

int
send_all(int socket, const void *buffer, size_t length, int flags)
{
ssize_t n;
const char *p = buffer;
while (length > 0)
{
n = send(socket, p, length, flags);
if (n <= 0)
return -1;
p += n;
length -= n;
}
return 0;
}

Note that if you are sending datagrams whose boundaries matter (e.g. UDP) you cannot use this technique; you have to treat a short send as a failure.

Invoking sendto() with wrong length

sendto() has no concept of the type of buffer you use for your messages. It only knows about the parameter values you give it, so it is your responsibility to make sure they are accurate.

If the length is smaller than the actual message, length number of bytes are sent for UDP, and up to length bytes are sent for TCP.

If the length is larger than the actual message, the behavior is undefined. At best, you will likely send random data from surrounding memory. At worse, you will crash your code when trying to send bytes from invalid memory.

Is recv(bufsize) guaranteed to receive all the data if sended data is smaller then bufsize?

Is the server side guaranteed to receive the data in one recv()?

For UDP, yes. recv() will return either 1 whole datagram, or an error. Though, if the buffer size is smaller than the datagram then the data will be truncated and you can't recover it.

For TCP, no. The only guarantee you have is that if no error occurs then recv() will return at least 1 byte but no more than the specified buffer size, it can return any number of bytes in between.

If not, how does the standard solution using header for specifying message length even works? The header itself could have been split and we have to check if header has been correctly received. Or the header is fixed length?

It can go either way, depending on the particular format of the header. Many protocols use fixed-length headers, and many protocols use variable-length headers.

Either way, you may have to call send() multiple times to ensure you send all the relevant bytes, and call recv() multiple times to ensure you receive all them. There is no 1:1 relationship between sends and reads in TCP.

Is the ravi's answer assuming that both client and server will receive what the other sent in a single recv()?

Ravi's answer makes no assumptions whatsoever about the number of bytes sent by send() and received by recv(). His answer is presented in a more higher-level perspective. But, it is very trivial to force the required behavior, eg:

int sendAll(int sckt, void *data, int len)
{
char *pdata = (char*) data;
while (len > 0) {
int res = send(sckt, pdata, len, 0);
if (res > 0) {
pdata += res;
len -= res;
}
else if (errno != EINTR) {
if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
return -1;
}
/*
optional: use select() or (e)poll to
wait for the socket to be writable ...
*/
}
}
return 0;
}

int recvAll(int sckt, void *data, int len)
{
char *pdata = (char*) data;
while (len > 0) {
int res = recv(sckt, pdata, len, 0);
if (res > 0) {
pdata += res;
len -= res;
}
else if (res == 0) {
return 0;
}
else if (errno != EINTR) {
if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
return -1;
}
/*
optional: use select() or (e)poll to
wait for the socket to be readable ...
*/
}
}
return 1;
}

This way, you can use sendAll() to send the message header followed by the message data, and recvAll() to receive the message header followed by the message data.



Related Topics



Leave a reply



Submit