В чем разница между read()
и recv()
, и между send()
и write()
в программировании сокетов с точки зрения производительности, скорости и других поведений?
В чем разница между read()
и recv()
, и между send()
и write()
в программировании сокетов с точки зрения производительности, скорости и других поведений?
Ответы:
Разница в том, что recv()
/ send()
работают только с дескрипторами сокетов и позволяют указывать определенные параметры для реальной операции. Эти функции немного более специализированы (например, вы можете установить флаг, чтобы игнорировать SIGPIPE
или отправлять внеполосные сообщения ...).
Функции read()
/ write()
- это универсальные функции файловых дескрипторов, работающие со всеми дескрипторами.
recv
и другое read
не доставит данные вызывающей стороне, но также не даст ошибки. Для звонящего поведение такое же. Вызывающий может даже не знать ничего о дейтаграммах (он может не знать, что это сокет, а не файл, он может не знать, что это сокет дейтаграммы, а не сокет потока). То, что датаграмма остается в ожидании, является неявным знанием того, как стеки IP работают в ядрах и невидимы для вызывающей стороны. С точки зрения вызывающих, они все равно будут обеспечивать равное поведение.
recv
? Причиной, по которой recv
и send
где в первую очередь была введена информация, был тот факт, что не все концепции дейтаграмм можно сопоставить с миром потоков. read
и write
обрабатывать все как поток данных, будь то канал, файл, устройство (например, последовательный порт) или сокет. Тем не менее, сокет является только реальным потоком, если он использует TCP. Если он использует UDP, он больше похож на блочное устройство. Но если обе стороны используют его как поток, он будет работать как поток, и вы даже не сможете отправить пустой пакет UDP с помощью write
вызовов, поэтому такая ситуация не возникнет.
read () эквивалентно recv () с параметром flags равным 0. Другие значения параметра flags изменяют поведение recv (). Аналогично, write () эквивалентно send () с флагами == 0.
recv
может быть использован только на сокете, и выдаст ошибку , если вы пытаетесь использовать его, скажем, STDIN_FILENO
.
read()
и write()
являются более общими, они работают с любым файловым дескриптором. Тем не менее, они не будут работать на Windows.
Вы можете передать дополнительные опции send()
и recv()
, поэтому вам, возможно, придется использовать их в некоторых случаях.
Я только недавно заметил, что когда я использовал write()
сокет в Windows, он почти работает (передаваемый FD write()
не совпадает с передаваемым send()
; я использовал _open_osfhandle()
для передачи FD write()
). Тем не менее, это не сработало, когда я попытался отправить двоичные данные, которые включали символ 10. write()
где-то вставил символ 13 до этого. Изменение этого send()
параметра с параметром flags, равным 0, решило эту проблему. read()
может иметь обратную проблему, если 13-10 последовательных в двоичных данных, но я не проверял это. Но это, кажется, другая возможная разница между send()
и write()
.
Еще одна вещь на Linux:
send
не позволяет работать на не-сокете fd. Таким образом, например, чтобы написать на USB-порт, write
необходимо.
«Производительность и скорость»? Разве это не ... синонимы здесь?
В любом случае, recv()
вызов принимает флаги, которые read()
этого не делают, что делает его более мощным или, по крайней мере, более удобным. Это одно из отличий. Я не думаю, что есть существенная разница в производительности, но я не проверял это.
В Linux я также заметил, что:
Прерывание системных вызовов и функций библиотеки обработчиками сигнала
Если обработчик сигнала вызывается, когда системный вызов или вызов функции библиотеки заблокированы, то либо:
вызов автоматически перезапускается после возврата обработчика сигнала; или
вызов не выполняется с ошибкой EINTR.
... Детали различаются в разных системах UNIX; ниже, подробности для Linux.
Если заблокированный вызов одного из следующих интерфейсов прерывается обработчиком сигнала, то вызов автоматически перезапускается после того, как обработчик сигнала возвращается, если использовался флаг SA_RESTART; в противном случае вызов завершается с ошибкой EINTR:
- вызовы read (2), readv (2), write (2), writev (2) и ioctl (2) на «медленных» устройствах.
.....
Следующие интерфейсы никогда не перезапускаются после прерывания обработчиком сигнала, независимо от использования SA_RESTART; они всегда терпят неудачу с ошибкой EINTR при прерывании обработчиком сигнала:
«Входные» сокетные интерфейсы, когда тайм-аут (SO_RCVTIMEO) был установлен на сокете с помощью setsockopt (2): accept (2), recv (2), recvfrom (2), recvmmsg (2) (также с ненулевым значением) аргумент тайм-аута) и recvmsg (2).
«Выходные» интерфейсы сокетов, когда для сокета было установлено время ожидания (SO_RCVTIMEO) с использованием setsockopt (2): connect (2), send (2), sendto (2) и sendmsg (2).
Проверьте man 7 signal
для более подробной информации.
Простое использование будет использовать сигнал, чтобы избежать recvfrom
блокировки на неопределенный срок.
Пример из 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);
}
#define write(...) send(##__VA_ARGS__, 0)
.