Read () ve recv () ile send () ve write () Arasındaki Fark nedir?


200

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?


3
Böyle uygulandığını yazma düşünün: #define write(...) send(##__VA_ARGS__, 0).
carefulnow1

Yanıtlar:


129

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 SIGPIPEveya 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.


3
Bu yanlıştır, 0 uzunluklu datagramlarda başka bir fark vardır - Sıfır uzunluklu bir datagram beklemede ise, sıfır bayrakları argümanı olan (2) ve recv () öğelerini farklı davranışlar sağlayın. Bu durumda read (2) 'nin etkisi yoktur (datagram beklemede kalır), recv () ise beklemedeki datagramı tüketir.
Abhinav Gauniyal

2
@AbhinavGauniyal Bu nasıl farklı davranışlar sağlar ? Bir 0 bayt veri birimi varsa, her ikisi de recvveread 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.
Mecki

2
@ Herkes için örtük bilgi olmayan mecki, örneğin beni al :)
Abhinav Gauniyal

1
@Mecki, 0 baytlık engellenmeyen başarılı okuma neyi gösterir? Datagram hala beklemede mi? Tam olarak ve sadece bu beni endişelendiriyor: bir datagramın başarıyla okunsa bile beklemede kalabileceği davranış. Durumun ortaya çıkıp çıkmayacağından emin değilim, bu yüzden bunu aklımda tutmak istiyorum.
sehe

2
@sehe Endişeleniyorsanız neden kullanmıyorsunuz recv? İlk etapta neden recvve sendnerede tanıtıldığı, tüm datagram kavramlarının akış dünyasına eşlenememesiydi. readve writeboru, 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.
Mecki

85

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.


31
Bütün hikaye bu değil. recvyalnızca bir soket üzerinde kullanılabilir ve bunu, örneğin, üzerinde kullanmaya çalışırsanız bir hata oluşturur STDIN_FILENO.
Joey Adams

77
Bu konu artık Google'daki ilk hit, Google stackoverflow'u seviyor
Eloff

12

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.


7

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().



6

Linux'ta başka bir şey:

sendsoket olmayan fd üzerinde çalışmaya izin vermez. Bu nedenle, örneğin usb portuna yazmak writegerekir.


2

"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.


15
Belki de bayraklarla uğraşmak zorunda kalmamak daha uygun olarak algılanabilir.
08:16

2

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 signalDaha fazla ayrıntı için kontrol edin .


Basit bir kullanım, recvfromsü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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.