2011-10-23 11 views
0

メールボックスサーバーをシミュレートする大きなプロジェクトを宿題として作成しました(同じコンピュータ上のプロセスを介して、FIFOを使用するので、宿題です)fifo上のデータの書き込み/読み取りの正しい方法

大きなファイル(たくさんあります)があるのでプロジェクトを投稿できませんが、データが失われたり、完全性が保持されないことがあります。

は、私はそれが多少間違っている、データを送信するためにこれらのコードスニペットを使用Network_IOは私が話している機能である:?

#include "Network.h" 

int Network_Open(const char* path,int oflag) 
{ 
    return open(path,oflag); 
} 

ssize_t Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize) 
{ 
    ssize_t retsize = 0; 
    ssize_t tmpDataSize = (ssize_t)dataSize; 
    errno = 0; 

    if (tmpDataSize == 0) return 0; 

    while ((retsize = (opcode == NetworkOpCode_Write? write(fifo,data,tmpDataSize) : read(fifo,data,tmpDataSize))) != tmpDataSize) 
    { 
     if (errno != EINTR) break; 
    } 

    return retsize; 
} 

Boolean Network_Send(int fifo,const void* data,size_t dataSize) 
{ 
    return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize); 
} 

Boolean Network_Receive(int fifo,void* data,size_t dataSize) 
{ 
    return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Read,data,dataSize); 
} 

Boolean Network_Close(int fifo) 
{ 
    if (fifo >= 0) 
     return close(fifo) == 0; 
} 

編集1:私は実際にテストするために使用していコードスニペットwriteケースでは

Boolean Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize) 
{ 
    ssize_t retsize = 0; 
    ssize_t tmpDataSize = (ssize_t)dataSize; 
    ssize_t sentDataSize = 0; 
    errno = 0; 

    if (tmpDataSize == 0) return True; 

    while (sentDataSize < tmpDataSize) 
    { 
     switch(opcode) 
     { 
      case NetworkOpCode_Write: 
       retsize = write(fifo,data + sentDataSize,tmpDataSize - sentDataSize); 
       break; 
      case NetworkOpCode_Read: 
       retsize = read(fifo,data + sentDataSize,tmpDataSize - sentDataSize); 
       break; 
     } 
     if (retsize < 0) 
     { 
      if (errno != EINTR) return False; 
      else 
      { 
       errno = 0; 
       continue; 
      } 
     } 
     sentDataSize += retsize; 
    } 

    if (errno != 0) 
     return False; 

    return sentDataSize == tmpDataSize; 
} 

Boolean Network_Send(int fifo,const void* data,size_t dataSize) 
{ 
    return Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize); 
} 

Boolean Network_Receive(int fifo,void* data,size_t dataSize) 
{ 
    return Network_IO(fifo,NetworkOpCode_Read,data,dataSize); 
} 

答えて

2

IMHO Network_IO()関数は目的を果たさない。 Network_Send()関数とNetwork_Receive()関数によって与えられた読取り/書込み呼び出しのオペコードを「逆多重化」することが唯一の目的です。 read()を呼び出し、Network_Send()およびNetwork_Receive()関数で直接書き込む方がよいでしょう。戻り値の型(ブール値)の選択も奇妙です。

read()とwrite()のエラー条件が異なる可能性があります。将来、EINTRのいずれかで処理する必要があるだけではありません。また、あなたのファンクションブロックは、彼らが実際に送ったり受け取ったりするまで戻りません。また、パイプやFIFOの場合、カーネルによって提供されるバッファースペースの量は非常に限られており、通常は1つのメモリーページです。これにより、リーダまたはライタが読み取りまたは書き込みをブロックする機会が増え、1ブロックのデータ転送あたり(少なくとも)2つのコンテキストスイッチが転送されます。

「ループが完了するまで」メソッド。 Matが提供するものは、標準的なやり方です。また、ゼロを返す読み書き用にも準備されている必要があります。

EDIT:部分的な読み取り/書き込みを処理する必要があることを意味するもの:をオフにして、バッファの残りの部分を送受信してからを開始する必要があります。

int mywrite(int fd, char *buff, size_t size) 
{ 
int rc; 
size_t done, todo; 

for (done=0; done < size;) { 
    todo = size - done; 
    rc = write (fd, buff+done, todo); 
    switch (rc) { 
    case -1: /* some read error: check it */ 
     switch(errno) { 
     case EINTR: continue; 
     /* ... maybe some other cases you need to handle */ 
     default: return -1; 
      } 
     break; 
    case 0: /* (in some cases) the other side closed the connection */ 
     /* handle it here; possibly return error */ 
     break; 
    default: /* the normal case */ 
     done += rc; 
     break; 
     } 
    } 
return done; 
} 
+0

でコードスニペットを見ることができるのかを見出そうとしています。あなたの答えが面白いと思うのですが、これは私のデザイン決定について議論する必要がないケースです。その選択の周りには多くのことがあります。私は書き込み/読み取り呼び出しを「ラッピングする」という決定を変更しません。 **ブロッキング**は実際に教師が学習目的で尋ねるものです。 –

+1

あなたのプログラムは「ほぼ正しい」というケースがあります。コンピュータプログラムを書き込んでデバッグすることはできますが、書き込むことは分かりません。デザインの選択に関する文章的なコメントは、批評を意図したものではなく、デザインが重要であることを認識させるためのものです。プログラミングは簡単ではなく、プログラムを設計することは簡単ではありません。 BTW:Linuxのマウント可能なファイルシステムは、関数ポインタ、多かれ少なかれオブジェクト指向の抽象化を実装しています。 – wildplasser

+0

デザインの背後にある主な問題は、宿題であり、 "先生が言うように"行う必要があることがあります。具体的にはループメソッドです。先生のメモで見つけることができるので、直接使用しましたが、完全に正しい、私はデータの "remaning部分"を送信する必要があると思うと述べたように、私はそれを試してみます。私はデザインに焦点を当てていますが...まあ、私はCを嫌い、たくさんの日に書くと気が狂っています –

2

、あなたのコードは

while ((retsize = write(fifo,data,tmpDataSize)) != tmpDataSize) { ... } 
に沸きます10

最初のwriteでは、1バイトだけが書き込まれるとします。その場合はtmpDataSize-1バイトをプッシュしようと次のwriteが必要です(data+1から開始)。しかし、あなたが今行うことは、最初のバイトを含むすべてを再送します。読み取りの場合の

while (bytesLeftToSend > 0) { 
sent = write(fifo, data, bytesLeftToSend); 
if (sent == -1) { 
    // report error and bail out 
} 
bytesLeftToSend -= sent; 
data += sent; 
} 

同じこと:

擬似コードでは、ロジックは次のようになります。

ところで、割り当て中は?:の構成は実際には読みにくいです。

+0

Mh、「古い」データを破棄し、それをすべて再送信する(ちょうど尋ねる)問題は何ですか?私の書き込み/読み取り操作が少しでも0を送信し続ける状況がありますが、それは非常に退屈です。なぜこれが起こるか考えていますか? –

+0

問題は、パイプのもう一方の側で、再送信しているかどうかを判断できないということです。アプリケーションで複数のゼロを送信する原因となるのは、コードだけです。あなたが実際に送信するデータや、読んでいるときにそれをどのように解凍するかについてあなたが気にしていないなら、何かが起こる可能性があります。 – Mat

+0

説明したような実装を試しましたが、現時点でうまく機能していないか、少なくとも私はいつもよりも多くのエラーを取得しています。私はなぜ、あなたが** Edit 1 ** –

関連する問題