Apa perbedaan antara read()
dan recv()
, dan antara send()
dan write()
dalam pemrograman soket dalam hal kinerja, kecepatan dan perilaku lainnya?
Apa perbedaan antara read()
dan recv()
, dan antara send()
dan write()
dalam pemrograman soket dalam hal kinerja, kecepatan dan perilaku lainnya?
Jawaban:
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.
recv
dan tidak read
akan 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.
recv
? Alasan mengapa recv
dan di send
mana diperkenalkan di tempat pertama adalah fakta bahwa tidak semua konsep datagram dapat dipetakan ke dunia aliran. read
dan write
memperlakukan 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 write
panggilan, sehingga situasi ini tidak akan muncul.
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.
recv
hanya dapat digunakan pada soket, dan akan menghasilkan kesalahan jika Anda mencoba menggunakannya, katakan STDIN_FILENO
,.
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.
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()
.
Hal lain di linux adalah:
send
tidak memungkinkan untuk beroperasi pada non-socket fd. Jadi, misalnya menulis di port usb, write
diperlukan.
"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.
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 recvfrom
pemblokiran 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);
}
#define write(...) send(##__VA_ARGS__, 0)
.