C で記述されたクライアントで 4 つの send() を実行すると、サーバーは read() を 4 回実行する必要があるのでしょうか、それとも最初の読み取りで 4 つの send() がすべて一緒に読み取られるのでしょうか?
3 に答える
あなたを少し助けるために、あなたは基本的にあなた自身の質問にすでに答えています。
いわば「段階的に」行う必要があります。
クライアント:
int len = strlen(Filename) + 1; //Mind the terminating 0.
send(sock, (const char *)&len, sizeof(int), 0);
send(sock, Filename, len, 0); //Sending the filename
send(sock, &FileSize, sizeof(int), 0);
send(sock, FileBuf, FileSize, 0);
このコードは途中でデータ全体を送信します (ファイル全体が「FileBuf」変数にあると仮定します)。
サーバ:
int len;
char *FileBuf, FileName[20];
recv(sock, &len, sizeof(int), 0); //Receives the filename length. (4 Bytes)
recv(sock, FileName, len, 0); //Receives the filename (x bytes)
recv(sock, &len, sizeof(int), 0); //Receives the file length (again, 4 bytes)
FileBuf = new char[len]; //Creates sufficient space in memory.
recv(sock, FileBuf, len, 0); //Receives the file into the appropriate variable.
これは絶対的な最低限のバリアントであり、あまり堅実ではありませんが、アイデアを得る必要があります。
より堅牢なアプローチでは、recv() と send() の戻り値を確認する必要があります。どちらも、この呼び出しで処理されたバイト数を返します。この値が「0」の場合、接続が相手側によって閉じられたことを意味します。(主にrecv()
)。-1 の場合は、何か問題が発生したことを意味し、errno
変数を確認する必要があります。
すべてがうまくいけば、送信した/受信しようとしたバイトの正確な量に等しくなります。
ただし、「len」(または 0 または -1) でない場合は、このような小さなラッパーを書くことができます。
unsigned char Recv(int sock, void *target, int Len) {
unsigned char *ptr = (unsigned char *)target, Ret = 0;
int RecvBytes = 1;
while(Len && !Ret) {
RecvBytes = recv(sock, ptr, Len, 0);
if(!RecvBytes) Ret = 1;
else if(RecvBytes == -1) Ret = errno;
else {
Len -= RecvBytes;
ptr += RecvBytes;
}
}
return Ret;
}
このコードの動作: 期待していたすべてのデータ (Len
パラメーター) を受信するか、エラーが発生するまで、受信を続けます。すべてがうまくいくと、「0」が返されます。これは で確認できますif(!Recv())
。
別の便利なラッパー関数 (いわばショートカット) は次のとおりです。
uint32_t RecvInt(int sock) {
uint32_t Ret;
Recv(sock, &Ret, sizeof(Ret));
return ntohl(Ret);
}
この関数は、符号なし int を 1 つだけ受け取り、エンディアンをネットワーク バイト オーダーからホスト バイト オーダーに修正します。(ネットワーク バイト オーダーは常にビッグ エンディアンです。ホスト バイト オーダーは多くの場合、リトル エンディアンです)
これらのラッパー関数を使用すると、コードは次のように変更できます。
uint32_t len;
char *FileBuf, FileName[20];
len = RecvInt(sock); //Receives the filename length. (4 Bytes)
Recv(sock, FileName, len); //Receives the filename (x bytes)
len = RecvInt(sock); //Receives the file length (again, 4 bytes)
FileBuf = new char[len]; //Creates sufficient space in memory.
Recv(sock, FileBuf, len); //Receives the file into the appropriate variable.
ストリーム ソケット (TCP など)の場合:送信側で行われた呼び出しのsend()
数に違いはありません。write()
データは、1 チャンクから 1 バイトの n チャンク (n は送信されたバイト数)、またはその間のチャンクで返される可能性があります。
データグラム ソケット (UDP など) の場合:recv()
またはの各recvmsg()
呼び出しは、相手側から送信された 1 つの完全なデータグラムを返します。recv()
またはの呼び出しの数recvmsg()
は、送信されたデータグラムの数と同じである必要があります。recv()
データグラムソケットから読み取る場合は推奨されread()
ますが、同じように動作するはずです。
との数は同じwrite()
でread()
ある必要はありません。write()
はすべてのデータを 1 つの部分に書き込みますが、他のコンピュータread()
ではそれをいくつかのチャンクでしか受信できない可能性があり、その逆も同様です。そのため、これらの関数の戻り値を常に確認し、部分的なデータ転送のみが発生した場合は、残りの送受信を続行する必要があります。