Яка різниця між read () та recv (), and between send () and write ()?


198

Яка різниця між read()і recv(), і між, send()і write()в програмуванні розетки з точки зору продуктивності, швидкості та іншої поведінки?


3
Подумайте про записи , як це реалізовано , як: #define write(...) send(##__VA_ARGS__, 0).
внимательно1

Відповіді:


128

Різниця полягає в тому, що recv()/ send()працюйте лише на дескрипторах сокет, і ви дозволяєте вказати певні параметри фактичної роботи. Ці функції трохи більш спеціалізовані (наприклад, ви можете встановити прапор для ігноруванняSIGPIPE або відправляти повідомлення поза межами діапазону ...).

Функції read()/ write()- це універсальні функції дескриптора файлів, що працюють на всіх дескрипторах.


3
Це невірно, є ще одна відмінність у разі дейтаграми довжиною 0 - Якщо очікувана дейтаграма нульової довжини, прочитайте (2) та recv () з аргументом нуля аргумент нуля забезпечують різну поведінку. У цьому випадку read (2) не впливає (дейтаграма залишається в очікуванні), тоді як recv () споживає очікувану дейтаграму.
Абхінав Ганіял

2
@AbhinavGauniyal Як би це забезпечувало різну поведінку ? Якщо є дейтаграма 0 байтів, обидва, recvі readвони не доставлятимуть дані абоненту, але також не буде помилок. Для абонента поведінка однакова. Абонент може навіть нічого не знати про дейтаграми (він може не знати, що це сокет, а не файл; він може не знати, що це сокет дейтаграми, а не сокет потоку). Те, що дейтаграма залишається в очікуванні, - це неявне знання про те, як IP-стеки працюють в ядрах і не видно абоненту. З точки зору того, хто телефонує, вони все ще забезпечуватимуть рівну поведінку.
Mecki

2
@Mecki, що не є чіткими знаннями для всіх, візьміть мене для прикладу :)
Abhinav Gauniyal

1
@Mecki, що вказує на незаблоковане успішне читання 0 байтів? Чи дієграма ще залишається в очікуванні? Саме це, і тільки це, мене хвилює: поведінка, яку дейтаграма може залишатися в очікуванні, навіть якщо вона успішно прочитана. Я не впевнений, чи може виникнути ситуація, про що я хотів би мати це на увазі.
sehe

2
@sehe Якщо ви переживаєте, чому б вам не скористатися recv? Причиною, де recvі sendде було запроваджено в першу чергу, був той факт, що не всі концепції дейтаграми можуть бути відображені у світі потоків. readі writeтрактувати все як потік даних, будь то труба, файл, пристрій (наприклад, послідовний порт) або сокет. Однак сокет - це лише справжній потік, якщо він використовує TCP. Якщо він використовує UDP, він більше схожий на блоковий пристрій. Але якщо обидві сторони використовують це як потік, він буде працювати як потік, і ви навіть не можете надіслати порожній пакет UDP, використовуючи writeдзвінки, тому така ситуація не виникне.
Mecki

85

За першим зверненням у Google

read () еквівалентно recv () з параметром flags 0. Інші значення параметра flags змінюють поведінку recv (). Аналогічно, write () еквівалентно send () з прапорами == 0.


31
Це не вся історія. recvможе використовуватися лише в сокеті, і викличе помилку, якщо спробувати використовувати його, скажімо, на STDIN_FILENO.
Джої Адамс

76
Ця нитка зараз є першим хітом у Google, Google любить
stackoverflow

12

read()і write()вони більш загальні, вони працюють з будь-яким дескриптором файлів. Однак вони не працюватимуть у Windows.

Ви можете передати додаткові параметри send()і recv(), тому , можливо , доведеться використовували їх в деяких випадках.


7

Нещодавно я помітив, що коли я використовував write()сокет у Windows, він майже працює (FD перейшов доwrite() не такий, як той, який передається send(); я раніше _open_osfhandle()передавав FD write()). Однак це не вийшло, коли я намагався надсилати бінарні дані, що включали символ 10. write()кудись вставлений символ 13 до цього. Змінивши його на send()параметр прапорів 0 вирішив цю проблему. read()може виникнути зворотна проблема, якщо 13-10 є послідовними у двійкових даних, але я не перевіряв її. Але це, мабуть, є іншою можливою різницею між send()та write().



6

Ще одна річ у Linux:

sendне дозволяє працювати на не-socket fd. Так, наприклад, писати на порту usb, writeнеобхідно.


3

"Продуктивність та швидкість"? Хіба тут не такі ... синоніми?

У будь-якому випадку, recv()виклик приймає прапори, які read()не роблять, що робить його більш потужним або, принаймні, більш зручним. Це одна різниця. Я не думаю, що є суттєва різниця в продуктивності, але не перевіряв її.


15
Можливо, не потрібно мати справу з прапорами, можливо, сприймається як зручніше.
semaj

2

У 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.