21

Cでのソケットプログラミングについて少し混乱しています。

ソケットを作成し、それをインターフェースとIPアドレスにバインドして、リッスンさせます。その上でいくつかのWebリソースを見つけ、それをうまく理解しました。特に、 Unixシステムでのネットワークプログラミングの記事は非常に有益であることがわかりました。

私を混乱させるのは、データがソケットに到着するタイミングです。

パケットがいつ到着するか、パケットの大きさをどのように知ることができますか?あなたは自分ですべての重労働をしなければなりませんか?

ここでの私の基本的な仮定は、パケットは可変長である可能性があるということです。したがって、バイナリデータがソケットに表示され始めたら、それからどのようにパケットを構築し始めますか?

4

4 に答える 4

17

簡単に言えば、あなたは自分ですべての重労働をしなければならないということです。読み取ることができるデータがあることを通知することはできますが、使用可能なバイト数はわかりません。可変長パケットを使用するほとんどのIPプロトコルでは、パケットの前に既知の固定長のヘッダーが追加されます。このヘッダーには、パケットの長さが含まれます。ヘッダーを読み取り、パケットの長さを取得してから、パケットを読み取ります。通信が完了するまで、このパターンを繰り返します(ヘッダーを読み取り、次にパケットを読み取ります)。

ソケットからデータを読み取るときは、特定のバイト数を要求します。読み取り呼び出しは、要求されたバイト数が読み取られるまでブロックされる場合がありますが、要求されたバイト数よりも少ないバイト数を返す可能性があります。これが発生した場合は、読み取りを再試行して、残りのバイトを要求するだけです。

ソケットから設定されたバイト数を読み取るための一般的なC関数は次のとおりです。

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}
于 2008-09-08T00:27:17.370 に答える
13

したがって、質問に対する答えは、トランスポートとしてUDPとTCPのどちらを使用しているかによってかなり異なります。

UDPの場合、必要なパケットサイズでrecv / recvfrom / recvmsgを呼び出すことができ(とにかくソースから固定長のパケットを送信する可能性があります)、データが利用可能であると想定できるため、作業ははるかに簡単になります。 、パケット長のサイズの倍数であります。(つまり、送信側のパケットのサイズでrecv *を呼び出すと、設定が完了します。)

TCPの場合、人生はもう少し興味深いものになります-この説明の目的のために、socket()、bind()、listen()、accept()の使い方をすでに知っていると仮定します-後者はあなたが得る方法です新しく作成された接続のファイル記述子(FD)。

ソケットのI/Oを実行する方法は2つあります。ブロッキングでは、read(fd、buf、N)を呼び出し、読み取りはそこにとどまり、Nバイトをbufに読み込むまで待機します。または非ブロッキングです。ここで、FDが読み取り可能かどうかを(select()またはpoll()を使用して)チェックし、次にread()を実行する必要があります。

TCPベースの接続を処理する場合、OSはパケットサイズに注意を払いません。これは、個別のパケットサイズのチャンクではなく、データの継続的なストリームと見なされるためです。

アプリケーションで「パケット」(渡されるパックまたはアンパックのデータ構造)を使用する場合は、適切なサイズの引数を指定してread()を呼び出し、一度にソケットからデータ構造全体を読み取ることができるはずです。対処しなければならない唯一の注意点は、送信元システムと宛先システムのバイトエンディアンが異なる場合に備えて、送信するデータを適切にバイトオーダーすることを忘れないことです。これは、UDPとTCPの両方に適用されます。

* NIXソケットプログラミングに関する限り、W。リチャードスティーブンスの「Unixネットワークプログラミング、Vol。1」(UNPv1)および「Unix環境での高度なプログラミング」(APUE)を強くお勧めします。前者は、トランスポートに関係なく、ネットワークベースのプログラミングに関する本であり、後者は、*NIXベースのプログラミングに適用される優れた万能プログラミングの本です。また、「TCP / IP Illustrated」、第1巻および第2巻を探してください。

于 2008-09-08T03:45:57.347 に答える
3

ソケットで読み取りを行うときは、読み取る最大バイト数を指定しますが、その数がない場合は、取得したバイト数を示します。部分的なパケットがあるかどうかがわかるように、プロトコルを設計するのはあなた次第です。たとえば、以前は可変長のバイナリデータを送信するときに、期待するバイト数を示すintを最初に配置していました。プロトコルで可能な最大のパケットよりも大きいバイト数を要求する読み取りを実行し、最初のintを受信したバイト数と比較して、処理するか、次の読み取りを試行します。 dは、完全なパケットを取得しました。

于 2008-09-08T00:05:22.073 に答える
1

ソケットは生のパケットよりも高いレベルで動作します。これは、読み取り/書き込みが可能なファイルのようなものです。また、ソケットから読み取ろうとすると、オペレーティングシステムは、要求を満たすデータが得られるまでプロセスをブロック(保留)します。

于 2008-09-08T00:06:01.133 に答える