Apa Perbedaan Antara read () dan recv (), dan Antara send () dan write ()?

198

Apa perbedaan antara read()dan recv(), dan antara send()dan write()dalam pemrograman soket dalam hal kinerja, kecepatan dan perilaku lainnya?

Sajad Bahmani
sumber
3
Pikirkan menulis seperti yang diterapkan seperti ini: #define write(...) send(##__VA_ARGS__, 0).
hati1 1

Jawaban:

128

Perbedaannya adalah bahwa recv()/ send()hanya bekerja pada deskriptor soket dan membiarkan Anda menentukan opsi tertentu untuk operasi yang sebenarnya. Fungsi-fungsi itu sedikit lebih khusus (misalnya, Anda dapat mengatur bendera untuk diabaikan SIGPIPE, atau untuk mengirim pesan out-of-band ...).

Fungsi read()/ write()adalah fungsi deskriptor file universal yang bekerja pada semua deskriptor.

Gonzalo
sumber
3
Ini tidak benar, ada satu perbedaan lain dalam hal datagram dengan panjang 0 - Jika datagram panjang-nol tertunda, baca (2) dan recv () dengan argumen bendera nol memberikan perilaku yang berbeda. Dalam keadaan ini, baca (2) tidak memiliki efek (datagram tetap tertunda), sementara recv () mengkonsumsi datagram tertunda.
Abhinav Gauniyal
2
@AbhinavGauniyal Bagaimana hal itu memberikan perilaku yang berbeda ? Jika ada datagram 0 byte, keduanya, recvdan tidak readakan mengirimkan data ke pemanggil tetapi juga tidak ada kesalahan. Bagi penelepon, perilakunya sama. Penelepon mungkin bahkan tidak tahu apa-apa tentang datagram (mungkin tidak tahu bahwa ini adalah soket dan bukan file, mungkin tidak tahu bahwa ini adalah soket datagram dan bukan soket aliran). Bahwa datagram tetap tertunda adalah pengetahuan implisit tentang bagaimana tumpukan IP bekerja di kernel dan tidak terlihat oleh pemanggil. Dari perspektif penelepon, mereka masih akan memberikan perilaku yang sama.
Mecki
2
@Mecki itu bukan pengetahuan implisit untuk semua orang, ambil saya misalnya :)
Abhinav Gauniyal
1
@Mecki apa yang ditunjukkan oleh pembacaan sukses tanpa byte 0 byte? Apakah datagram masih tertunda? Tepatnya, dan hanya itu, yang mengkhawatirkan saya: perilaku yang datagram bisa tetap tertunda meskipun berhasil dibaca. Saya tidak yakin apakah situasinya dapat muncul, yang mana saya ingin mengingatnya.
sehe
2
@ Sehe Jika Anda khawatir, mengapa Anda tidak menggunakannya recv? Alasan mengapa recvdan di sendmana diperkenalkan di tempat pertama adalah fakta bahwa tidak semua konsep datagram dapat dipetakan ke dunia aliran. readdan writememperlakukan segala sesuatu sebagai aliran data, apakah itu pipa, file, perangkat (misalnya port serial) atau soket. Namun soket hanya aliran nyata jika menggunakan TCP. Jika menggunakan UDP itu lebih seperti perangkat blok. Tetapi jika kedua belah pihak menggunakannya seperti aliran, itu akan berfungsi seperti aliran dan Anda bahkan tidak dapat mengirim paket UDP kosong menggunakan writepanggilan, sehingga situasi ini tidak akan muncul.
Mecki
85

Per klik pertama di Google

read () sama dengan recv () dengan parameter flags 0. Nilai lain untuk parameter flags mengubah perilaku recv (). Demikian pula, write () sama dengan mengirim () dengan flag == 0.

Jonathan Feinberg
sumber
31
Ini bukan keseluruhan cerita. recvhanya dapat digunakan pada soket, dan akan menghasilkan kesalahan jika Anda mencoba menggunakannya, katakan STDIN_FILENO,.
Joey Adams
76
Utas ini sekarang menjadi hit pertama di Google, Google menyukai stackoverflow
Eloff
12

read()dan write()lebih umum, mereka bekerja dengan deskriptor file apa pun. Namun, mereka tidak akan berfungsi di Windows.

Anda dapat memberikan opsi tambahan ke send()dan recv(), jadi Anda mungkin harus menggunakannya dalam beberapa kasus.

Bastien Léonard
sumber
7

Saya baru-baru ini memperhatikan bahwa ketika saya menggunakan write()soket pada Windows, ia hampir berfungsi (FD yang diteruskan write()tidak sama dengan yang dilewati send(); saya biasa _open_osfhandle()menerima FD yang dilewati write()). Namun, itu tidak berfungsi ketika saya mencoba mengirim data biner yang menyertakan karakter 10. write()suatu tempat memasukkan karakter 13 sebelum ini. Mengubahnya menjadi send()dengan parameter flags 0 memperbaiki masalah itu. read()bisa memiliki masalah sebaliknya jika 13-10 berturut-turut dalam data biner, tapi saya belum mengujinya. Tapi itu tampaknya menjadi perbedaan lain yang mungkin antara send()dan write().

ajb
sumber
2
+1. Lihat juga winsock tidak mendukung baca / tulis
Joseph Quinsey
6

Hal lain di linux adalah:

sendtidak memungkinkan untuk beroperasi pada non-socket fd. Jadi, misalnya menulis di port usb, writediperlukan.

Mert Mertce
sumber
3

"Performa dan kecepatan"? Bukankah itu semacam ... sinonim, di sini?

Pokoknya, recv()panggilan itu mengambil bendera yang read()tidak, yang membuatnya lebih kuat, atau setidaknya lebih nyaman. Itu satu perbedaan. Saya tidak berpikir ada perbedaan kinerja yang signifikan, tetapi belum diuji untuk itu.

beristirahat
sumber
15
Mungkin tidak harus berurusan dengan bendera mungkin dianggap lebih nyaman.
semaj
2

Di Linux saya juga memperhatikan bahwa:

Gangguan panggilan sistem dan fungsi perpustakaan oleh penangan sinyal
Jika penangan sinyal dipanggil saat panggilan sistem atau panggilan fungsi perpustakaan diblokir, maka:

  • panggilan secara otomatis dimulai kembali setelah penangan sinyal kembali; atau

  • panggilan gagal dengan EINTR kesalahan.

... Detail bervariasi di seluruh sistem UNIX; di bawah ini, detail untuk Linux.

Jika panggilan yang diblokir ke salah satu antarmuka berikut diinterupsi oleh penangan sinyal, maka panggilan secara otomatis dimulai kembali setelah penangan sinyal kembali jika bendera SA_RESTART digunakan; jika tidak panggilan gagal dengan EINTR kesalahan:

  • read (2), readv (2), write (2), writev (2), dan ioctl (2) memanggil perangkat "lambat".

.....

Antarmuka berikut tidak pernah dihidupkan ulang setelah terganggu oleh penangan sinyal, terlepas dari penggunaan SA_RESTART; mereka selalu gagal dengan EINTR kesalahan ketika diinterupsi oleh penangan sinyal:

  • Antarmuka soket "Input", ketika batas waktu (SO_RCVTIMEO) telah diset pada soket menggunakan setockopt (2): accept (2), recv (2), recvfrom (2), recvmmsg (2) (juga dengan non-NULL argumen timeout), dan recvmsg (2).

  • Antarmuka soket "Output", ketika batas waktu (SO_RCVTIMEO) telah ditetapkan pada soket menggunakan setockopt (2): sambungkan (2), kirim (2), kirimto (2), dan sendmsg (2).

Memeriksa man 7 signal lebih detail.


Penggunaan sederhana akan menggunakan sinyal untuk menghindari recvfrompemblokiran tanpa batas.

Contoh dari 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);
}
Rick
sumber