What is the difference between read()
and recv()
, and between send()
and write()
in socket programming in terms of performances, speed and other behaviors?

- 8,390
- 14
- 74
- 124

- 17,325
- 27
- 86
- 108
-
5Think of write as implemented like this: `#define write(...) send(##__VA_ARGS__, 0)`. – carefulnow1 Jan 07 '17 at 09:39
9 Answers
The difference is that recv()
/send()
work only on socket descriptors and let you specify certain options for the actual operation. Those functions are slightly more specialized (for instance, you can set a flag to ignore SIGPIPE
, or to send out-of-band messages...).
Functions read()
/write()
are the universal file descriptor functions working on all descriptors.
-
8This is incorrect, there's one other difference in case of datagrams of 0 length - If a zero-length datagram is pending, read(2) and recv() with a flags argument of zero provide different behavior. In this circumstance, read(2) has no effect (the datagram remains pending), while recv() consumes the pending datagram. – Abhinav Gauniyal Feb 11 '17 at 08:44
-
2@AbhinavGauniyal How would that provide _different behavior_? If there's a 0 byte datagram, both, `recv` and `read` will deliver no data to the caller but also no error. For the caller, the behavior is the same. The caller may not even know anything about datagrams (it may not know that this is a socket and not a file, it may not know that this is a datagram socket and not a stream socket). That the datagram stays pending is implicit knowledge about how IP stacks work in kernels and not visible to the caller. From caller perspective, they will still provide equal behavior. – Mecki Jul 19 '17 at 16:51
-
2@Mecki that's not implicit knowledge for everyone, take me for example :) – Abhinav Gauniyal Jul 20 '17 at 12:12
-
@Mecki if the datagram stays pending in only the `read` scenario, wouldn't that cause repeated `read`s to repeatedly "succeed" with zero-length reads (so, infinitely)? – sehe Sep 20 '18 at 11:48
-
@sehe A `read` of 0 bytes without error is documented as "end-of-stream". So yes, you can keep reading as long as you won't but the system already told you that you are at the end thus you cannot expect any more data to be read. For `recv` the same is documented for stream sockets (e.g. TCP). Datagram sockets (e.g. UDP) have no beginning and no end, so returning zero bytes has no specified meaning for them. – Mecki Sep 20 '18 at 13:26
-
2@Mecki what does a non-blocking successful read of 0 bytes indicate? Does the datagram still stay pending? Exactly that, and only that, is worrying me: the behaviour that a datagram can stay pending even if successfully read. I'm not sure whether the situation can arise, which is wy I'd like to keep it in mind. – sehe Sep 20 '18 at 13:28
-
2@sehe If you are worried, why don't you use `recv`? The reason why `recv` and `send` where introduced in the first place was the fact that not all datagram concepts could be mapped to the world of streams. `read` and `write` treat everything as a stream of data, whether it is a pipe, a file, a device (e.g. a serial port) or a socket. Yet a socket is only a real stream if it uses TCP. If it uses UDP it's more like a block device. But if both sides use it like a stream, it will work like a stream and you cannot even send an empty UDP packet using `write` calls, so this situation won't arise. – Mecki Sep 20 '18 at 15:31
-
1@Mecki that's the approach I was gravitating to. It's just that lots of legacy code use the `read` style interface and I'd rather to be more sure that this is actually ok (with non-blocking reads). Thank you for your clarifying remarks. That's really helpful. I love when people take the time to share these bits of hard-earned experience. Cheers! – sehe Sep 20 '18 at 15:34
-
OP is wrong about the read(0) differences not matting, see which just taught me this https://spectrum-os.org/lists/hyperkitty/list/devel@spectrum-os.org/thread/7UEZD4KF3XZE74S4F7BR2KZL5ZWHTWQ3/ For a UnixDomain datagram socket which is reliable, the buffer can fill up with empty packets, and send/write will block. read(0) means no progress, recv(0) means progress. – Ericson2314 Jul 13 '20 at 00:06
read() is equivalent to recv() with a flags parameter of 0. Other values for the flags parameter change the behaviour of recv(). Similarly, write() is equivalent to send() with flags == 0.

- 44,698
- 7
- 80
- 103
-
36This isn't the whole story. `recv` can only be used on a socket, and will produce an error if you try to use it on, say, `STDIN_FILENO`. – Joey Adams Jul 31 '11 at 05:29
-
99
-
1@JoeyAdams: On most systems, recv will work just fine on a non-socket (such as STDIN_FILENO). There are only a few systems on which it will fail (such as windows). – Chris Dodd May 04 '23 at 00:06
read()
and write()
are more generic, they work with any file descriptor.
However, they won't work on Windows.
You can pass additional options to send()
and recv()
, so you may have to used them in some cases.

- 60,478
- 20
- 78
- 95
I just noticed recently that when I used write()
on a socket in Windows, it almost works (the FD passed to write()
isn't the same as the one passed to send()
; I used _open_osfhandle()
to get the FD to pass to write()
). However, it didn't work when I tried to send binary data that included character 10. write()
somewhere inserted character 13 before this. Changing it to send()
with a flags parameter of 0 fixed that problem. read()
could have the reverse problem if 13-10 are consecutive in the binary data, but I haven't tested it. But that appears to be another possible difference between send()
and write()
.

- 1,045
- 1
- 11
- 33

- 31,309
- 3
- 58
- 84
-
4+1. See also [winsock not supporting read/write](http://stackoverflow.com/q/4778043) – Joseph Quinsey Jun 19 '14 at 15:14
-
Windows also doesn't receive a UDP packet with less than 3 bytes of data, so, there's issues with Windows. :) – JonS Aug 20 '20 at 00:28
Another thing on linux is:
send
does not allow to operate on non-socket fd. Thus, for example to write on usb port, write
is necessary.

- 1,614
- 3
- 21
- 32
On Linux I also notice that :
Interruption of system calls and library functions by signal handlers
If a signal handler is invoked while a system call or library function call is blocked, then either:
the call is automatically restarted after the signal handler returns; or
the call fails with the error EINTR.
... The details vary across UNIX systems; below, the details for Linux.
If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call is automatically restarted after the signal handler returns if the SA_RESTART flag was used; otherwise the call fails with the error EINTR:
- read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
.....
The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler:
"Input" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): accept(2), recv(2), recvfrom(2), recvmmsg(2) (also with a non-NULL timeout argument), and recvmsg(2).
"Output" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): connect(2), send(2), sendto(2), and sendmsg(2).
Check man 7 signal
for more details.
A simple usage would be use signal to avoid recvfrom
blocking indefinitely.
An example from APUE:
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#define BUFLEN 128
#define TIMEOUT 20
void
sigalrm(int signo)
{
}
void
print_uptime(int sockfd, struct addrinfo *aip)
{
int n;
char buf[BUFLEN];
buf[0] = 0;
if (sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)
err_sys("sendto error");
alarm(TIMEOUT);
//here
if ((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0) {
if (errno != EINTR)
alarm(0);
err_sys("recv error");
}
alarm(0);
write(STDOUT_FILENO, buf, n);
}
int
main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err;
struct sigaction sa;
if (argc != 2)
err_quit("usage: ruptime hostname");
sa.sa_handler = sigalrm;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) < 0)
err_sys("sigaction error");
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_DGRAM;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
err_quit("getaddrinfo error: %s", gai_strerror(err));
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
if ((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0)) < 0) {
err = errno;
} else {
print_uptime(sockfd, aip);
exit(0);
}
}
fprintf(stderr, "can't contact %s: %s\n", argv[1], strerror(err));
exit(1);
}

- 7,007
- 2
- 49
- 79
"Performance and speed"? Aren't those kind of ... synonyms, here?
Anyway, the recv()
call takes flags that read()
doesn't, which makes it more powerful, or at least more convenient. That is one difference. I don't think there is a significant performance difference, but haven't tested for it.

- 391,730
- 64
- 469
- 606
-
16Perhaps not having to deal with flags may be perceived as more convenient. – semaj Nov 24 '09 at 16:16
The only difference between recv() and read() is the presence of flags. With a zero flags argument, recv() is generally equivalent to read()

- 21
- 3
you can use write() and read() instead send() and recv() but send() and recv() offer much greater control over your data transmission

- 21
- 3