Яка різниця між 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()
параметр прапорів 0 вирішив цю проблему. read()
може виникнути зворотна проблема, якщо 13-10 є послідовними у двійкових даних, але я не перевіряв її. Але це, мабуть, є іншою можливою різницею між send()
та write()
.
Ще одна річ у Linux:
send
не дозволяє працювати на не-socket 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) за допомогою setockopt (2): accept (2), recv (2), recvfrom (2), recvmmsg (2) (також з не-NULL аргумент тайм-ауту) та recvmsg (2).
"Вихідні" інтерфейси сокетів, коли в сокеті встановлено тайм-аут (SO_RCVTIMEO) за допомогою setockopt (2): підключити (2), відправити (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)
.