68user's page 掲示板

Prev< No. 1517> Next  [最新発言に戻る] [過去ログ一覧]
No. 1517 # 68user 2001/01/07 (日) 02:21:32
>>1510 E田
構造体の受け渡しに関しては、僕の出る幕はなさげですが、
一応まとめておきます。まず、E田さんはポインタの使い方を
学んで下さい。rosegarden さんのソースは
      tes_s.test1 = ((unsigned long *)ptr)[0];
      tes_s.test2 = ((unsigned short *)ptr)[1];
となっていますが、rosegarden さん自身が説明されている通り
これではまずいので
      tes_s.test1 = *(unsigned long *)(ptr);
      tes_s.test2 = *(unsigned short *)(ptr+4);
の方がいいでしょう。わからなければ再度質問してください。

で、それを理解してからやっとバイトオーダの話になります。これは
今回の件では関係ないかもしれない (エンディアンによってたまたま
問題が顕在化していないかもしれない) ので、参考程度にどうぞ。

以下のソースでは配列 data から変数 test1/2/3 に値を代入しようと
しています。

#include <stdio.h>
#include <string.h>

void
my_memcpy(char *dst, char *src, size_t len){
    src += len-1;
    while (len--) *dst++ = *src--;
}

main(){
    unsigned long test1;
    unsigned short test2;
    unsigned short test3;
    char data[] = {0x12,0x34,0x56,0x78,0x0,0x1,0x0,0x2};
    char *ptr = data;

    test1 = *(unsigned long *)(ptr);
    test2 = *(unsigned short *)(ptr+4);
    test3 = *(unsigned short *)(ptr+6);
    printf("普通に代入 0x%x 0x%x 0x%x\n", test1, test2, test3);

    memcpy(&test1, ptr+0, sizeof(test1));
    memcpy(&test2, ptr+4, sizeof(test2));
    memcpy(&test3, ptr+6, sizeof(test3));
    printf("memcpy 0x%x 0x%x 0x%x\n", test1, test2, test3);

    test1 = htonl(*(unsigned long *)(ptr));
    test2 = htons(*(unsigned short *)(ptr+4));
    test3 = htons(*(unsigned short *)(ptr+6));
    printf("hton して代入 0x%x 0x%x 0x%x\n", test1, test2, test3);

    my_memcpy(&test1, ptr+0, sizeof(test1));
    my_memcpy(&test2, ptr+4, sizeof(test2));
    my_memcpy(&test3, ptr+6, sizeof(test3));
    printf("逆順にmemcpy 0x%x 0x%x 0x%x\n", test1, test2, test3);
}

このサンプルでは
    char data[] = {0x12,0x34,0x56,0x78,0x0,0x1,0x0,0x2};
となっていますが、これを適当に切り取って変数に代入すると
順序が狂ってしまいます。実行結果は以下の通り。
    普通に代入 0x78563412 0x100 0x200 (間違い)
    memcpy 0x78563412 0x100 0x200 (間違い)
    hton して代入 0x12345678 0x1 0x2 (正しい)
    逆順にmemcpy 0x12345678 0x1 0x2 (正しい)
前の2つは順序が逆になっています。これはリトルエンディアンマシン
(x86 など) で発生します。ビッグエンディアン (68000, Sparc など)
では起こりません。

後の2つは htons/htonl や自作の my_memcpy でバイト順を置換して
代入しています (もちろん hton を使う方がよい)。

これはソケット経由でデータを送ると、バイト順が狂うという
意味ではありません。エンディアンが異なるマシン間でも、
データは送った順序で届きます。ですから、同じエンディアン
同士でデータを送りあえば問題は顕在化しません。

ただし、
    「異なるエンディアン間で変数の値を直接送信したとき」
は、
    「一方の CPU 内部ではバイト順の交換が行われるのに、
        他方では行われない」
ので、バイト順が狂ってしまいます。ですから、
    「事前にネットワークバイトオーダに変換してから送信」
し、
    「受信側はネットワークバイトオーダと認識して変数に代入する」
のが望ましいということです。

実際のソースで書くと、以下のような感じになります。

    送受信側でバイトオーダが一致していれば OK。異なるなら NG。
        (送信側)
        long send_num=0x12345678L;
        write(socket, &send_num, sizeof(send_num));
        (受信側)
        long recv_num;
        read(socket, &recv_num, sizeof(recv_num));

    バイト列をソケット経由で変数に代入する。受け手側が
    リトルエンディアンなら OK。そうでなければ NG。
        (送信側)
        char buf[]={0x12,0x34,0x56,0x78}; /* 0x12345687 を送りたいとする */
        write(socket, buf, sizeof(buf));
        (受信側)
        long recv_num;
        read(socket, &recv_num, sizeof(recv_num));

    ネットワークバイトオーダで送信。ネットワークバイトオーダを
    ホストバイトオーダにして代入。これが一番よい。
        (送信側)
        long send_num = htonl(0x12345678L);
        write(socket, &send_num, sizeof(send_num));
        (受信側)
        long recv_num;
        char buf[256];
        read(socket, buf, sizeof(recv_num));
        recv_num = ntohl(*(long *)(buf));

なお、ネットワークバイトオーダ=ビッグエンディアンのオーダです。
別に両者で合意が取れていればいいので、リトルエンディアンで
統一したければそれはそれで構いません。

ちなみに X プロトコルでは高速化のため、事前にクライアント・サーバ
間でバイトオーダが異なるかどうかチェックして、
    - 同じバイトオーダならそのままデータを送る
    - 異なるバイトオーダならネットワークバイトオーダに変換してから送る
となっているとか。

ところで
    my_memcpy(void *dst, void *src, size_t len){
とすると gcc に invalid use of void expression と怒られるんですが、
引数を void * で受けるのってできないんでしたっけ?

>>1512 gixs
> Linuxのselect(2)は、戻った時、第5引数の値が残り時間を示して戻ってくる
> (タイムアウトしたら値はゼロになる) ので、waitvalの値の設定はwhile
> ループ内でやる必要があります(manにも書いてあります)。
ご指摘どうもです。その通りですね。ちなみに FreeBSD では
select(2) の BUGS の項で、本来上記のような動作をするべきだが、
現状ではそうなっていない (waitval の値は書き換えられない) と
あります。

>>1513 CoreFighter
> NICにはMACアドレスってのがありますよね。
> モデムやTAにもMACアドレスってあるのでしょうか?
ないです。なぜかっちゅうと NIC は Ethernet の端点だからです。
# なぜ Ethernet だと MAC アドレスが必要で、モデム/TA に
# MAC アドレスが必要ないか…は、うまく説明できないなぁ。
# 誰か教えて下さい。

Prev< No. 1517> Next  [最新発言に戻る] [過去ログ一覧]