68user's page 掲示板

Prev< No. 1497〜1513> Next  [最新発言に戻る] [過去ログ一覧]
No. 1497 # ふくし 2001/01/03 (水) 20:06:29
あけましておめでとうございます。本年もよろしくお願いします。
別のことをやっていて偶然発見したんですが、

------------------------------ foo
#! perl
use strict;

my $n = 17; # 普通の変数
print "---> $\$n\n"; # リファレンスしていきなりデリファレンス

------------------------------
% perl -w foo
Use of uninitialized value in concatenation (.) or string at foo line 5.
---> 17

結果は確かに出てるんだけど、なんかundefの変数を二重引用符に入れたときと同じ
警告メッセージが出ます。これはなぜでしょう?

No. 1498 # mm 2001/01/03 (水) 22:29:52
>結果は確かに出てるんだけど、
↓を試してみて下さい。
my $n = 17; # 普通の変数
$\ = '<OUTPUT RECODE SEPARATOR>';
print "---> $\$n\n"; # リファレンスしていきなりデリファレンス

↓こっちも
my $n = 17; # 普通の変数
print "---> ${\$n}\n"; # リファレンスしていきなりデリファレンス

No. 1499 # 68user 2001/01/04 (木) 16:53:48
>>1494 CoreFighter
> BIOSの設定画面で、MPSってありますけど
> Versionの1.1と1.4の違いってなんでしょうか
MultiProcessor Specification の略ですが、詳しいことはさっぱりです。
現状では ACPI に取って変わられたんじゃないかな。具体的なことは
intel のサイトあたりに仕様書が転がってるかもしれません。

No. 1500 # ふくし 2001/01/04 (木) 17:39:57
>mmさん
あー、、、(^^;;;;; すいませんでした。
どうもありがとうございます!

No. 1501 # E田 2001/01/04 (木) 17:43:36
UNIXで、C言語で、TCP/IPを使って、クライアント/サーバ(マルチクライアントがアクセスする。非ブロッキングという)プログラムをしています。
selectを使用して、特定の、クライアントが接続しているソケットのタイムアウトを知りたいのですが、その方法が、良くわかりません。
こちらの方が詳しいということなので、ぜひ教えてください。
お願いします。本を読んでも、良くわかりませんでした。

No. 1502 # 68user 2001/01/04 (木) 17:54:58
>>1501 E田
現状はどこまでわかっているんですか?

何もわからないなら、まずは
    http://X68000.startshop.co.jp/~68user/net/c-echo-1.html
を読んでください。で、ソケットを使えるようになると。

で、perl ですが、select の使い方はこんな感じ。イメージをつかんで下さい。
    http://X68000.startshop.co.jp/~68user/net/echo-4.html

んで、C での select(2) の使い方。
    http://home.jp.FreeBSD.ORG/cgi-bin/showmail/FreeBSD-users-jp/54916
    http://home.jp.FreeBSD.ORG/cgi-bin/showmail/FreeBSD-users-jp/54917

わからなければ、作ってるソースから余計な部分をそぎ落としたソースを
公開して、再度質問してください。

No. 1503 # inpaku 2001/01/04 (木) 20:50:44
こんにちは。ネットワークプログラミングについて質問があるのですが…。
現在、Debian Linuxをもちいて、簡単なプログラミングから練習している
のですが、いき詰まりました。
PF_PACKETをつかってデータリンクに直接データを渡したいのですが、
ethhdrのh_dest や h_sourceにどうやってMACアドレスを入れて良いか
解らなくなりました。IPアドレスでは、inet_ptonとかあったんですが、
MACアドレスの場合もあるのですか??
宜しくお願いします。

No. 1504 # 68user 2001/01/04 (木) 23:22:14
>>1503 inpaku
> 簡単なプログラミングから練習しているのですが、いき詰まりました。
ははぁ、「簡単なプログラミングから練習」で、いきなりデータリンク層ですか。
僕にはちょっと荷が重いなぁ。

確認ですが、非 TCP/UDP かつ 非 IP のデータを送りたいのですよね?

UNIX Magazine 2000年7月号「BSD をハックする - 齊藤明紀」で、
    - 非 IP のプロトコルを使うにはどうすればよいか
    - 送信する Ethernet パケットに含まれる MAC アドレスを自由に
        設定することはできるか
について、NetBSD での解説が書かれています。

また、「UNIX ネットワークプログラミング第2版 Vol.1」
    http://X68000.startshop.co.jp/~68user/net/link-book.html#8
では、データリンクへのアクセス手法として、
    - BSD の BPF (BSD Packet Filter)
    - SVR4 の DLPI (SVR4 Data Link Provider Interface)
    - Linux の SOCK_PACKET
の3つがあげられています。どうやらここらへんは OS により
API がまちまちらしいですね。実際、手元の FreeBSD 4.2-BETA
では (PF|AF|SOCK)_PACKET という定数は定義されていません。

で、この本によると Linux の SOCK_PACKET を使うなら
    fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
とすると、データリンクからすべてのフレームを受けとれる
らしいです。

あと、tcpdump が使用している libpcap というライブラリが
上記3種の方法に対応しているようなので、参考になるかも
しれません。
# libpcap は読み出ししかできませんが、送信に対応させるのは
# それほど面倒ではないとか。

> IPアドレスでは、inet_ptonとかあったんですが、
> MACアドレスの場合もあるのですか??
BSD には libc に ether_aton とか ether_hostton てのがあります。
Linux はどうですか?

くぅ、面白い話題だけど、調べるのに一杯一杯だ。

No. 1505 # inpaku 2001/01/05 (金) 00:07:02
68userさんへ
親切にお答え下さって、ありがとうございます。
ether_aton とか ether_hostton ですか、探してみます。
実は僕もUNIX ネットワークプログラミング第2版 Vol.1を
持ってますが、はっきりいって、C言語すらおぼつかない僕
には辛いです(笑)
たしかにLinuxではSOCK_PACKETを使うと本には書いてあり
ましたが、Kernel2.2以降?からはPF_PACKETを使えと、man
ページに書いてあったものですから、それを使ってます。
また、libpcapのソースも落としましたが、書き換えるのは
僕には不可能でした(苦笑)
ところで、僕は何をしたかったのかというと、自分で任意の
プロトコルをつくってそれで通信させてみようということだ
ったんです。無謀とお思いでしょうが、やらなきゃならんの
です。卒業のために…。とにかくがんばってみます。
ありがとうございました。

No. 1506 # E田 2001/01/05 (金) 11:52:25
早速、回答してくださいまして、ありがとうございます。
どこまでわかっているか、ということなので書きますが、
ソケットの生成とかは、できています。
サンプルとかを真似して、クライアント/サーバで動かしてみました。
fork()で、マルチクライアントにもできています。
でも、select()を組み合わせると、よくわかりません。
動作は、教えてもらったページとかで、なんとなくわかるのですが、
それをCでやろうとすると、わからなくなります。
select[1]とかと書いて、タイムアウトの時間を指定してやると、
それをすぎても読み出せない時にはタイムアウトしたっていうことで
検出できるんですか?
よろしくお願いします。

No. 1507 # E田 2001/01/05 (金) 11:54:12
追伸です。
ソースとかは、まだ、ほとんど、サンプルとかのechoサーバとかなので、
書きませんでした。

No. 1508 # E田 2001/01/05 (金) 12:14:52
すみません、もう一つ質問です。
送信するデータで、データの最初に全データの長さが入っていて、
その次に、データのIDが入っていて、その後にデータの本文が
入っているというものを受信する時の方法についてなんですが、
それを読む時、最初のデータの長さとIDを読んで、その後で
データの本文を読みたいのですが、構造体みたいなのを用意して、
そこに格納しようと思っています。
データの長さはu_longで4byteで、
IDは、0x00100101(u_longで4byte)とかという番号で、
データの本文は、u_shortで2byte+longで4byte+char[8]です。
socketでの送信用のchar型からキャストしてみようと
しているんですが、最初の00がうまく行きません。
(送信する時のキャストはうまく行っています。受信する時は、
ちゃんと全部、もとのままのデータを受け取っています。)
これは、socketの扱い方の方のhton()とかでやらないと
いけないのですか?
それから、もしかすると、Cの方の書き方が下手糞なせいなのかも
しれないのですが、構造体に格納するのがうまく行かないので、
たとえばで良いので、もしも良かったら、例を教えてくれませんか?
お願いします。

No. 1509 # 68user 2001/01/05 (金) 12:56:19
> 最初の00がうまく行きません。
うまく行かないプログラムを (余計な部分は削った上で)
公開してください。

はい、C+select の超手抜きサンプル。バグありまくりですが
一応動きます。細部は参考にせず、おおまかな流れを見て下さい。

------------------
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUF_LEN 256 /* バッファのサイズ */

int main( int argc , char *argv[]){
    int connected_socket[100];
    int listening_socket;
    struct sockaddr_in sin;
    int sock_optval = 1;
    int port = 5000;
    char buf[BUF_LEN];
    struct timeval waitval;
    fd_set fd;
    fd_set org_fd;
    int max_sock = 0;
                                                                /* リスニングソケットを作成 */
    listening_socket = socket(AF_INET,SOCK_STREAM,0);

                                                                /* ソケットオプション設定 */
    if ( setsockopt(listening_socket,SOL_SOCKET,SO_REUSEADDR,
                                    &sock_optval,sizeof(sock_optval)) == -1 ){
        perror("setsockopt");
        exit(1);
    }
                                                                /* アドレスファミリ・ポート番号・IPアドレス設定 */
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if ( bind(listening_socket,(struct sockaddr *)&sin,sizeof(sin)) < 0 ){
        perror("bind");
        exit(1);
    }

    if ( listen(listening_socket, SOMAXCONN) == -1 ){
        perror("listen");
        exit(1);
    }
    printf("ポート %d を見張ります。\n",port);

    waitval.tv_sec = 1;
    waitval.tv_usec = 0;

    FD_ZERO(&org_fd);
    FD_SET(listening_socket, &org_fd);
    max_sock = listening_socket;
        
    while (1){
        int i;
        struct hostent *peer_host;
        struct sockaddr_in peer_sin;

        memcpy(&fd, &org_fd, sizeof(org_fd));

        select(max_sock+1, &fd, NULL, NULL, &waitval);

        for ( i=0 ; i<=max_sock ; i++ ){
            if ( FD_ISSET(i, &fd) ){
                if ( i == listening_socket ){
                    int len;
                    len = sizeof sin;
                    max_sock++;
                    printf("connected_socket[%d]\n",max_sock);
                    connected_socket[max_sock] =
                        accept(listening_socket, (struct sockaddr *)&sin, &len);

                    if ( connected_socket[max_sock] == -1 ){
                        perror("accept failed.\n");
                    }
                        
                    len = sizeof(peer_sin);
                    getpeername(connected_socket[max_sock], (struct sockaddr *)&peer_sin,&len);
                        
                    peer_host = gethostbyaddr((char *)&peer_sin.sin_addr.s_addr,
                                                                        sizeof(peer_sin.sin_addr),AF_INET);
                        
                    printf("接続: %s [%s] ポート %d\n",
                                  peer_host->h_name,
                                  inet_ntoa(peer_sin.sin_addr),
                                  ntohs(peer_sin.sin_port)
                                  );
                    FD_SET(max_sock, &org_fd);

                } else {
                    int read_size;
                    read_size = read(connected_socket[i], buf, sizeof(buf)-1);
                    if ( read_size == 0 ){
                        printf("接続が切れました。引き続きポート %d を見張ります。\n",port);
                        close(connected_socket[i]);
                        FD_CLR(i, &org_fd);
                    } else {
                        printf("メッセージ: %s",buf);
                        write(connected_socket[i],buf,strlen(buf));
                    }
                }
            }
        }
    }
    close(listening_socket);
    return 0;
}

No. 1510 # E田 2001/01/05 (金) 16:07:43
もう、お返事を頂けたとは! すごいです。
ありがとうございます。
参考にして、勉強してみます。
それと、構造体に入れるところのプログラムを、そこのところだけ書きます。

テスト用に、こういう構造体を作りました。
struct test_s{
    u_long test_1;
    u_short test_2;
    u_short test_3;
    u_short test_4;
    u_short test_5;
};
struct test_s tes;

それから、読み込むところのプログラムです。

    while(1){
        int len;
        char *ptr;
        char buf1[256];

        len = read( newsockfd, buf1, sizeof( buf1 ));
        buf1[len] = '\0';
        if( len > 0 ){
            if( strncmp( buf1, "end", 3 ) == 0 ){
                break;
            }
            ptr = buf1;

            tes.test_1 = (int)ptr[0];
            tes.test_2 = (atoi)ptr[1];
            tes.test_3 = (atol)ptr[2];
            tes.test_4 = (int)ptr[3];
            tes.test_5 = (int)ptr[4];
        }
    }

こんな感じです。

test_2以降に入る予定のデータは、みんな同じのを送っていますけど、
どれも、変な感じになってしまいます。
(ここには書いていないですけど、printf()で表示させています。)
構造体とかポインタとか、意味は勉強したんですが、
書くのは初めてなので、そのせいかなとも思うんですが、
キャストのやり方が違うのかもしれません。
いろいろやっているうちに、自分ではわからなくなってしまいました。
ほんとうにすみませんけれども、教えてください。
お願いします。

No. 1511 # rosegarden 2001/01/06 (土) 00:15:40
>>E田
>キャストのやり方が違うのかもしれません。
確かにおかしな部分はあるようです。もっとも、それがどの程度
全体に影響を及ぼしているかは分かりません。参考程度と言うことで。
まず、次のようなサンプルプログラムを用意します。

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

struct test_s {
    unsigned long test1;
    unsigned short test2;
    unsigned short test3;
    unsigned short test4;
    unsigned short test5;
} tes_s;

int
main(int argc, char *argv[])
{
    char buff[256];
    char *ptr;

    buff[0] = '\x12'; buff[1] = '\x34'; buff[2] = '\x56'; buff[3] = '\x78';
    buff[4] = '\x0'; buff[5] = '\x1';
    buff[6] = '\x0'; buff[7] = '\x2';
    buff[8] = '\x0'; buff[9] = '\x3';
    buff[10] = '\x0'; buff[11] = '\x4';

    ptr = buff;
    tes_s.test1 = ((unsigned long *)ptr)[0];
    tes_s.test2 = ((unsigned short *)ptr)[1];
    tes_s.test3 = ((unsigned short *)ptr)[2];
    tes_s.test4 = ((unsigned short *)ptr)[3];
    tes_s.test5 = ((unsigned short *)ptr)[4];

    return 0;
}

なおキャストの仕方がE田さんのとは違いますが、
E田さんの意図を汲むのなら上の方がおそらく良いでしょう。

これをデバッガで検査してみました。最後の手前で break させて
変数を見ます。

(gdb) x/100bx ptr
0xbfbfd6f0: 0x12 0x34 0x56 0x78 0x00 0x01 0x00 0x02
0xbfbfd6f8: 0x00 0x03 0x00 0x04 0x44 0xd7 0xbf 0xbf

これを見る限りデータはちゃんとセットされています。

(gdb) p/x tes_s.test1
$1 = 0x78563412

最初の 4 バイトはひっくり返っています。もしも、動作させる予定の
計算機の CPU が little endian なら memcpy などを使って 1byte ずつ
コピーした方が無難です。ただし、SPARC とか m68k なら気にしなくて良い
場合もあります。(ただし、完璧に機種依存になるので、その旨コメントで
明記した方が良いでしょう。)

(gdb) p/x tes_s.test2
$2 = 0x7856
(gdb) p/x tes_s.test3
$3 = 0x100
(gdb) p/x tes_s.test4
$4 = 0x200
(gdb) p/x tes_s.test5
$5 = 0x0
(gdb) q

次からは、ずれていますね。例えば、tes_s.test2 = ((unsigned short*)ptr)[1]
というのは最初から、short が並んでいるとして、最初から 2 番目のものを
とりだすことになるので 配列先頭からの 3 バイト目と 4 バイト目をとりだ
します。更に、バイトオーダが絡んで来るので、話しは複雑になります。
機種に依存して良いのなら、

union hoge {
            struct some_struct {
              ....
            } hogehoge;
            char buff[256];
}

などとして一気にコピーする手法が典型的ですが、バイトオーダに悩まされま
す。これをすると SPARC では動くが intel 系の CPU では動かない、あるい
はその逆のプログラムになります。

まるで、馬鹿みたいに思えるかも知れませんが、memcpy で地道に値のコピー
を行った方が良いです。

繰り返しますが、これをなおしたとしても、
E田さんの問題の解決になるとは限りませんので、あらかじめおふくみおき下
さい。あくまでも気がついた範囲ではと言う話です。

No. 1512 # gixs 2001/01/06 (土) 01:05:05
>>1509 68user
68userさんはご存知かもしれませんが、他の人がはまらないように。
Linuxのselect(2)は、戻った時、第5引数の値が残り時間を示して戻ってくる(タイムアウトしたら値はゼロになる)ので、waitvalの値の設定はwhileループ内でやる必要があります(manにも書いてあります)。

> コピーした方が無難です。ただし、SPARC とか m68k なら気にしなくて良い
> 場合もあります。(ただし、完璧に機種依存になるので、その旨コメントで
> 明記した方が良いでしょう。)
教育的観点から言っても、やはり「常にネットワークバイトオーダに」でしょう。

> まるで、馬鹿みたいに思えるかも知れませんが、memcpy で地道に値のコピー
> を行った方が良いです。
バイトオーダと構造体のパディングを考えると、これしかありませんね。

参考
http://www.kt.rim.or.jp/~ksk/sock-faq/unix-socket-faq-ja-2.html#ss2.15

データ型をやりとりしたいなら、構造体ひとつに対し専用の読みだしと書き出しの関数を作るのが常套手段です。
(内部的には、構造体のメンバ変数をチマチマとネットワークバイトオーダにしながら、バッファにバイト列として書き出します。send側)。

もうひとつの手は(書かなかったら、68userさんが指摘するでしょうが)、数値でもなんでも文字列にしてしまう手です。
クライアント側のテストをスクリプト言語やtelnetを使って簡単にできるので、お薦めです(ただし、簡単すぎて卒業研究っぽくならないかもしれませんが)。

No. 1513 # CoreFighter 2001/01/06 (土) 02:11:36
68userさん MPSの件有難う御座いました。

ところで今疑問に思っている事があるのですが、
NICにはMACアドレスってのがありますよね。
モデムやTAにもMACアドレスってあるのでしょうか?

#ものすごい些細な質問で・・すんません。

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