Arasındaki fark nedir read()
ve recv()
ve aralarında send()
ve write()
performansları, hız ve diğer davranışları bakımından soket programlamada?
Arasındaki fark nedir read()
ve recv()
ve aralarında send()
ve write()
performansları, hız ve diğer davranışları bakımından soket programlamada?
Yanıtlar:
Fark şudur recv()
/ send()
yalnızca soket tanımlayıcılarında çalışır ve gerçek işlem için belirli seçenekleri belirlemenize izin verir. Bu işlevler biraz daha uzmanlaşmıştır (örneğin, yoksaymak SIGPIPE
veya bant dışı mesajlar göndermek için bir bayrak ayarlayabilirsiniz ...).
Fonksiyonlar read()
/ write()
, tüm tanımlayıcılar üzerinde çalışan evrensel dosya tanımlayıcı fonksiyonlardır.
recv
veread
arayan değil, aynı zamanda hiçbir hata hiçbir veri teslim edecek. Arayan için davranış aynıdır. Arayan, datagramlar hakkında hiçbir şey bilmeyebilir (bunun bir dosya değil, bir soket olduğunu bilemeyebilir, bunun bir akış soketi değil, bir datagram soketi olduğunu bilemeyebilir). Datagramın beklemede kalması, IP yığınlarının çekirdeklerde nasıl çalıştığı ve arayan tarafından görülemediği hakkında örtük bir bilgidir. Arayan perspektifinden bakıldığında, yine de eşit davranış sağlayacaktır.
recv
? İlk etapta neden recv
ve send
nerede tanıtıldığı, tüm datagram kavramlarının akış dünyasına eşlenememesiydi. read
ve write
boru, dosya, aygıt (örneğin seri bağlantı noktası) veya yuva olsun, her şeyi bir veri akışı olarak ele alın. Ancak bir soket yalnızca TCP kullanıyorsa gerçek bir akıştır. UDP kullanıyorsa, daha çok bir blok cihaz gibidir. Ancak her iki taraf da bir akış gibi kullanırsa, akış gibi çalışır ve write
çağrıları kullanarak boş bir UDP paketi bile gönderemezsiniz , bu nedenle bu durum ortaya çıkmaz.
Google'daki ilk isabet başına
read (), flags parametresi 0 olan recv () öğesine eşdeğerdir. flags parametresi için diğer değerler, recv () davranışını değiştirir. Benzer şekilde, write (), == 0 bayrakları ile send () öğesine eşdeğerdir.
recv
yalnızca bir soket üzerinde kullanılabilir ve bunu, örneğin, üzerinde kullanmaya çalışırsanız bir hata oluşturur STDIN_FILENO
.
read()
ve write()
daha genel, herhangi bir dosya tanımlayıcı ile çalışırlar. Ancak, Windows üzerinde çalışmazlar.
Sen ek seçenekler geçebilir send()
ve recv()
bazı durumlarda onları kullanmış olabilir bu yüzden.
Son zamanlarda write()
Windows'ta bir soket kullandığımda neredeyse işe yaradığını fark ettim (FD'nin geçtiği ile write()
aynı olmadığını send()
; _open_osfhandle()
FD'nin geçmesini sağladım write()
). Ancak, 10 karakterini içeren ikili veri göndermeye çalıştığımda işe yaramadı write()
. Bundan önce 13 karakterini ekledi. send()
0 olan flags parametresiyle değiştirmek bu sorunu çözdü. read()
İkili verilerde 13-10 ardışıksa ters sorun olabilir, ancak test etmedim. Ancak bu, send()
ve arasındaki başka bir olası fark gibi görünüyor write()
.
Linux'ta başka bir şey:
send
soket olmayan fd üzerinde çalışmaya izin vermez. Bu nedenle, örneğin usb portuna yazmak write
gerekir.
"Performans ve hız"? Bu tür eşanlamlılar değil mi?
Her neyse, recv()
çağrı read()
yapmayan bayrakları alır , bu da onu daha güçlü veya en azından daha kullanışlı hale getirir. Bu bir fark. Önemli bir performans farkı olduğunu düşünmüyorum, ancak test etmedim.
Linux'ta şunu da fark ettim:
Sistem çağrıları ve kütüphane işlevlerinin
sinyal işleyicileri tarafından kesilmesi Bir sistem çağrısı veya kütüphane işlev çağrısı engellendiğinde bir sinyal işleyici çağrılırsa, aşağıdakilerden birini yapın:
sinyal işleyici döndükten sonra çağrı otomatik olarak yeniden başlatılır; veya
çağrı EINTR hatasıyla başarısız olur.
... Ayrıntılar UNIX sistemlerinde değişiklik gösterir; Aşağıda, Linux için ayrıntılar.
Aşağıdaki arabirimlerden birine engellenen bir çağrı bir sinyal işleyici tarafından kesintiye uğrarsa, SA_RESTART bayrağı kullanılmışsa, sinyal işleyici geri döndükten sonra çağrı otomatik olarak yeniden başlatılır; aksi takdirde çağrı EINTR hatasıyla başarısız olur:
- "yavaş" cihazlarda read (2), readv (2), write (2), writev (2) ve ioctl (2) çağrıları.
.....
Aşağıdaki arabirimler, SA_RESTART kullanılmasına bakılmaksızın, bir sinyal işleyici tarafından kesildikten sonra asla yeniden başlatılmaz; bir sinyal işleyici tarafından kesildiğinde her zaman EINTR hatasıyla başarısız olurlar:
Soket üzerinde setsockopt (2) kullanılarak bir zaman aşımı (SO_RCVTIMEO) ayarlandığında "Giriş" soket arabirimleri: kabul (2), recv (2), recvfrom (2), recvmmsg (2) (ayrıca NULL olmayan) zaman aşımı argümanı) ve recvmsg (2).
Soket üzerinde setsockopt (2) kullanılarak bir zaman aşımı (SO_RCVTIMEO) ayarlandığında "Çıkış" soket arabirimleri: bağlan (2), gönder (2), sendto (2) ve sendmsg (2).
man 7 signal
Daha fazla ayrıntı için kontrol edin .
Basit bir kullanım, recvfrom
süresiz engellemeyi önlemek için sinyal kullanmak olacaktır .
APUE'dan bir örnek :
#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)
.