回答:
違いは、recv()
/ はsend()
ソケット記述子でのみ機能し、実際の操作に特定のオプションを指定できることです。これらの関数は少し特殊化されています(たとえば、フラグを設定して無視SIGPIPE
したり、帯域外メッセージを送信したりできます...)。
関数read()
/ write()
は、すべての記述子で機能するユニバーサルファイル記述子関数です。
recv
およびread
、発信者だけでなく、エラーなしに何もデータを提供しません。呼び出し元の場合、動作は同じです。呼び出し元はデータグラムについてさえ何も知らないかもしれません(これがファイルではなくソケットであることを知らないかもしれません。これがデータグラムソケットであってストリームソケットではないことを知らないかもしれません)。データグラムが保留のままであるということは、カーネルでIPスタックがどのように機能するかに関する暗黙の知識であり、呼び出し元からは見えません。発信者の観点からは、それらは依然として同等の動作を提供します。
recv
か?最初に導入された理由recv
とsend
場所は、すべてのデータグラム概念がストリームの世界にマッピングできるわけではないという事実でした。read
そしてwrite
それは、パイプ、ファイル、デバイス(例えばAシリアルポート)またはソケットであるかどうか、データのストリームとして扱うすべてのもの、。しかし、ソケットは、TCPを使用する場合、実際のストリームにすぎません。UDPを使用する場合は、ブロックデバイスのようになります。ただし、両側でストリームのように使用すると、ストリームのように機能し、write
呼び出しを使用して空のUDPパケットを送信することもできないため、このような状況は発生しません。
read()
そしてwrite()
彼らは、任意のファイルディスクリプタで動作する、より一般的なものです。ただし、Windowsでは機能しません。
追加のオプションをsend()
およびに渡すことができるrecv()
ため、場合によってはそれらを使用する必要があります。
最近write()
、Windowsのソケットで使用するとほとんど機能することに気づきました(渡されるFDはに渡されるFD write()
と同じではありませんsend()
。以前_open_osfhandle()
は、FDをに渡していたためですwrite()
)。ただし、文字10を含むバイナリデータを送信しようとしたときに機能しませんでした write()
。この前に文字13が挿入されました。send()
flagsパラメータを0に変更して、この問題を修正しました。 read()
バイナリデータで13〜10が連続している場合、逆の問題が発生する可能性がありますが、テストしていません。しかし、それは、間に別の可能性の違いのように見えるsend()
とwrite()
。
Linuxのもう1つは次のとおりです。
send
非ソケットfdを操作することはできません。したがって、たとえば、USBポートに書き込むためにwrite
必要です。
「パフォーマンスとスピード」?これらの類義語はここではないですか?
とにかく、recv()
呼び出しは受け取らread()
ないフラグを取るので、より強力に、または少なくともより便利になります。それが1つの違いです。パフォーマンスに大きな違いはないと思いますが、テストはしていません。
Linuxでは、次のことにも気づきます。
シグナルハンドラーによるシステムコールとライブラリー関数の中断
システムコールまたはライブラリー関数の呼び出しがブロックされているときにシグナルハンドラーが呼び出された場合は、次のいずれかを実行します。
シグナルハンドラが戻った後、呼び出しは自動的に再開されます。または
呼び出しはエラーEINTRで失敗します。
...詳細はUNIXシステムによって異なります。以下に、Linuxの詳細を示します。
次のいずれかのインターフェイスへのブロックされた呼び出しがシグナルハンドラーによって中断された場合、SA_RESTARTフラグが使用されていれば、シグナルハンドラーが戻った後に呼び出しが自動的に再開されます。そうでない場合、呼び出しはエラーEINTRで失敗します。
- 「遅い」デバイスでのread(2)、readv(2)、write(2)、writev(2)、およびioctl(2)呼び出し。
.....
次のインターフェイスは、SA_RESTARTの使用に関係なく、シグナルハンドラーによって中断された後は再起動されません。シグナルハンドラーによって中断されると、常にエラーEINTRで失敗します。
「入力」ソケットインターフェイス、setsockopt(2)を使用してソケットにタイムアウト(SO_RCVTIMEO)が設定されている場合:accept(2)、 recv(2)、 recvfrom(2)、recvmmsg(2)(これもNULL以外)タイムアウト引数)、およびrecvmsg(2)。
「出力」ソケットインターフェイス。setsockopt(2)を使用してソケットにタイムアウト(SO_RCVTIMEO)が設定されている場合:connect(2)、send(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);
}
#define write(...) send(##__VA_ARGS__, 0)
。