talkd

Table of Contents

basic socket

前回まで、talkなんていう、プチ時代遅れのアプリを解剖してきた。で、ソケッ ト回りの理解が、おぼつかない事が判明。ならば基本に戻ってソケットの理解 から。この間、本棚から発掘した本のサンプルを試してみる。

sakae@fb:/tmp/src/chap2 $ cc server.c
server.c:17:1: warning: type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int [-Wimplicit-int]
   17 | main()
      | ^
      | int
server.c:34:3: warning: call to undeclared library function 'exit' with type 'void (int) __attribute__((noreturn))'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
   34 |                 exit(1);
      |                 ^
server.c:34:3: note: include the header <stdlib.h> or explicitly provide a declaration for 'exit'
server.c:50:2: warning: call to undeclared function 'close'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
   50 |         close(soc_waiting);
      |         ^
 :
5 warnings generated.

20年前のやつだと、コンパイラーと整合が取れていない。ちゃんと警告を潰し ておく。

#include <unistd.h>
#include <stdlib.h>

を追加してからmain回りを改修した。これで現代風になった。

サーバー側の接続用ソケット

(gdb)
39              if(bind(soc_waiting, (struct sockaddr *)&me, sizeof(me)) == -1){
(gdb) p me
$2 = {
  sin_len = 0 '\000',
  sin_family = 2 '\002',
  sin_port = 20675,       # = 0x50c3
  sin_addr = {
    s_addr = 0
  },
  sin_zero = "\000\000\000\000\000\000\000"
}

ポート番号は、コード上は50000なんで、それを16進数に変換。

(gdb) p/x 50000
$5 = 0xc350

糞石はリトルエンディアンなので、ビッグエンディアンに脳内変換して、それ を10進数にすると、 sin_port の番号になる。

下記は、OSの把握する情報

sakae@fb:/tmp/src/chap2 $ netstat -4
Active Internet connections
Proto Recv-Q Send-Q Local Address          Foreign Address        (state)
tcp4       0      0 fb.50000               fb.56916               ESTABLISHED
tcp4       0      0 fb.56916               fb.50000               ESTABLISHED
tcp4       0     96 fb.ssh                 192.168.1.5.58448      ESTABLISHED

その時のクライアント側のソケット。

(gdb) p server
$2 = {
  sin_len = 0 '\000',
  sin_family = 2 '\002',
  sin_port = 20675,
  sin_addr = {
    s_addr = 2231478464
  },
  sin_zero = "\000\000\000\000\000\000\000"
}

クライアント側で、接続情報をどうやって、引き出すか? 下記の様にすれば 良い。

struct sockaddr_in peek;
socklen_t plen;

connect(soc, (struct sockaddr *)&server, sizeof(server));
getsockname(soc, (struct sockaddr *)&peek, &plen);
printf( "getsockname: addr = %s, port = %d\n",
  inet_ntoa(peek.sin_addr),
  ntohs(peek.sin_port)
);

尚、サーバー側で引き出すには、getpeername を使えとな。

それから、ソケットの大事な特性として、データの流れは双方向で同時に可能 というのがある。全2重通信ね。でも、ソケット番号は一つだ。stdin,stdout の様に方向が決められていないんだ。

n = read(0, buf, BUF_LEN);   /* 標準入力0から読む     */
write(soc, buf, n);          /* ソケットsocに書き出す */
n = read(soc, buf, BUF_LEN); /* ソケットsocから読む   */
write(1, buf, n);            /* 標準出力1に書き出す   */

これ、データのやりとりの一部だけど、同一ディスクリプタsocに対して、 write/readが可能になってる。

もちろん、これを実現する為には、ハード的に信号ラインが2系統になってる。 光ケーブルなら2本のファイバーがセットになっている。書き込み用ラインと 読み込み用ラインね。それ ぞれが一方通行だけど。

光ケーブルと言えば、最近面白い話を聞いた。残念ながらURLは失念しちゃっ たけど、内容はこんな話だ。つい最近、世界最高気温が更新されて60度になっ たそうだ。そこで心配になるのは、光ケーブルの高温障害らしい。温度上昇に より、ケーブルの特性が変化しその結果、エラーが頻発するそうだ。にわかに、 信じられないな。熊谷方面にお住まいの方、報告宜しく。今年の夏は暑いらし いですから。

マシン間の通信

上記は、同一マシン内での出来事だった。同一マシン内なら、わざわざインターネットを利用しな くても、別な方法で可能だ。例えばパイプやそれを発展させた機構が有る。しかし、別マシンのプロ セスと通信しようとしたら、普通は、インターネットを利用する。

そんな事なんで、FreeBSD(fb)をサーバーとし、OpenBSD(ob)の方をクライアン トとして、接続実験。

at fb (server)

sakae@fb:~ $ netstat -4
Active Internet connections
Proto Recv-Q Send-Q Local Address          Foreign Address        (state)
tcp4       0      0 fb.50000               ob.28431               ESTABLISHED

ローカルってのは自分のマシンの事。フォリンってのは別なマシンを意味する。 今はfbなマシンで確認してるので、ローカルな所にfbが表われている。そして、 50000は、そのローカルマシン内でプロセス(アプリ)を区別す為に付けられた 識別番号だ。これがインターネット上のアプリを唯一に識別する方法になって いる。尚、OS上からみると、プロセスを識別する為の番号として、プロセス番 号ってのが有るのは、当然の事。何でも番号に置き換えて表現するって言った ら、ユーザーを識別するuidなんてのもある。これなんか、政府がさかんに宣 伝してる、マイナンバーそのものである。

at ob (client)

ob$ netstat -f inet
Active Internet connections
Proto   Recv-Q Send-Q  Local Address          Foreign Address        TCP-State
tcp          0      0  ob.28431               fb.50000               ESTABLISHED
Active Internet connections
Proto   Recv-Q Send-Q  Local Address          Foreign Address
udp          0      0  ob.17347               time.cloudflare..ntp
udp          0      0  ob.32503               x.ns.gin.ntt.net.ntp
:

obなマシンでは、ntpdが動作してるんで、精度の高い時刻サーバーとの接続も、 垣間見られる。

この時のやりとりをパケットキャプチャーしてみた。

ob$ tcpdump -r NOM | cut -b -75
07:03:01.018943 ob.my.domain.28431 > fb.50000: S 2712367259:2712367259(0) w
07:03:01.026564 fb.50000 > ob.my.domain.28431: S 2809207120:2809207120(0) a
07:03:01.026593 ob.my.domain.28431 > fb.50000: . ack 1 win 256 <nop,nop,tim
07:03:23.219277 fb.50000 > ob.my.domain.28431: P 1:11(10) ack 1 win 1027 <n
07:03:23.409477 arp who-has fb tell ob.my.domain
07:03:23.409480 ob.my.domain.28431 > fb.50000: . ack 11 win 256 <nop,nop,ti
07:03:23.526492 arp reply fb is-at 12:34:56:78:9a:bc
07:03:37.691286 ob.my.domain.28431 > fb.50000: P 1:9(8) ack 11 win 256 <nop
07:03:37.736868 fb.50000 > ob.my.domain.28431: . ack 9 win 1027 <nop,nop,ti

クライアントからポート番号が公開されてるサーバーに接続。後はギッタン・ バッタンの交互送信が普通だ。

途中で、珍しいパケット、arpが割り込んできた。これはIPアドレスとイーサ ネット番号との対応を求める要求だ。同一サブネット内では、通信の最下層で、 イーサネット番号(簡単に言うと、NICのシリアル番号)が、利用されているん だ。ボードの交換に供えて、20分間隔ぐらいで、再問い合わせをする事になっ てる。 ボードを交換されちゃうと、シリアル番号が代わる。だから、定期的に最新の 番号に更新してる訳だ。

上で出てきた、プロセス番号とインターネット上でのプロセス識別、IPアド レスとイーサネット番号のように、2重構造で管理してる事が多いぞ。こんが らかるな。

talkd

次は、talkの取次をやってくれる、talkdを調べてみる。ダエモン君なので、 端末を持っていない。だから、今迄の得意技であるgdbを繰り出す訳にはいか ない。困った脳。talkd(8)を見ても、特別なスイッチは用意されていない。

しょうがないので、ソースに当たれ。 /usr/src/libexec/talkd/talkd.c

main(int argc, char *argv[])
{
        openlog("talkd", LOG_PID, LOG_DAEMON);
        if (gethostname(hostname, sizeof(hostname)) == -1) {
                syslog(LOG_ERR, "gethostname: %m");
                _exit(1);
        }
        :
        if (argc > 1 && strcmp(argv[1], "-d") == 0)
                debug = 1;

ダエモン君と言ってもmainが有ってホッとする。物の本の説明では、inetdが ネットワークの総合受付を担当し、該当するダエモン君にパケットを転送して くれるそうだ。そして、パケットは、ソケット番号を0として渡すという規約があるらし い。ここら辺の事情は昔盛んにやったcgiと一緒だな(と、遠い昔を懐かしんで おります。今の人にcgiなんて言っても通じないだろうけど)。とほほのCGI入門

それはそうと、-dと言うオプションを付けて起動されると、debugフラグがon になり、print文が自動的に活性化されるって、読める。

ob$ grep ntalkd /etc/inetd.conf
ntalk   dgram   udp     wait    root    /usr/libexec/ntalkd     ntalkd -d

でも、端末は無い。どこに出力する? それはsyslogファイルに出力される。 その為の仕掛けが、openlogの宣言みたい。これを最初に実行しておくと、出 力先が、syslogになる。

setup syslog

syslogを扱かうのは、syslogdと言う、これまたダエモン君だ。彼はOS起動時 から常駐している。で、syslogdの使い方を勉強すると言う、泥縄式、かっこ 良く言うと、オンデマンドな勉強ね。

どう制御するかは、/etc/syslog.confに設定しておけと。

talkdの中で、どの様に使われているか、調べて、syslog.confに反映って流れ。 職人さんみたいに、見て技を盗めってことだ。

table.c

if (debug)
        syslog(LOG_DEBUG, "find_match: not found");

if (debug)
        print_request("find_request", request);

これが2つの利用方法。

print_request(char *cp, CTL_MSG *mp) {
         :
        syslog(LOG_DEBUG, "%s: %s: id %d, l_user %s, r_user %s, r_tty %s",
            cp, tp, mp->id_num, mp->l_name, mp->r_name, mp->r_tty);

print_request は、ラッパーになっている。

cron.info                                    /var/cron/log
daemon.info                                  /var/log/daemon
daemon.debug                                 /var/log/daemon  ## add
ftp.info                                     /var/log/xferlog

cron/daemon/ftpとかの大枠と、情報の重要性(emarg/err/crit/info/debug)の 組み合わせで、どのファイルに出力するか決定してるようだ。

今回は、daemonに出力する様に、一行追加。そして、syslogdを再起動して、 設定を有効にする。

なお、これらのログファイルは、cronから起動されるnewsyslogコマンドの対 象になっている。独自のファイルに出力したい場合は、この事も考慮したら安 心だ。

real communication

準備万端整ったので、talkしてみる。

ob$ ps awx | grep talk
39953 ??  Sp       0:00.00 ntalkd -d
43082 p2  I+p      0:00.01 talk joy@fb

ダエモン君が起動してるな。彼は端末から切り離されているんで、端末欄が?? になっている。

ob$ netstat -f inet
Active Internet connections
Proto   Recv-Q Send-Q  Local Address          Foreign Address        TCP-State
tcp          0      0  ob.21886               fb.31636               ESTABLISHED

こちらは、FreeBSD側。ちとOpenBSDとは趣きが違う。

sakae@fb:~ $ ps awx | grep talk
968 u0  I+    0:00.08 talk:   (talk)
sakae@fb:~ $ netstat -4
Active Internet connections
Proto Recv-Q Send-Q Local Address          Foreign Address        (state)
tcp4       0      0 fb.31636               ob.21886               ESTABLISHED
udp4       0      0 fb.63500               *.*

そして、肝心のdebugログ相当。

/var/log/daemon

Mar 24 08:44:26 ob talkd[39953]: process_request: look_up: id 0, l_user joy, r_user sakae, r_tty
Mar 24 08:44:26 ob talkd[39953]: find_match: look_up: id 0, l_user joy, r_user sakae, r_tty
Mar 24 08:44:26 ob talkd[39953]: find_match: not found
Mar 24 08:44:26 ob talkd[39953]: process_request: look_up: not_here, id 0
Mar 24 08:44:26 ob talkd[39953]: process_request: announce: id -1, l_user joy, r_user sakae, r_tty
Mar 24 08:44:26 ob talkd[39953]: find_request: announce: id -1, l_user joy, r_user sakae, r_tty ttyp0
Mar 24 08:44:26 ob talkd[39953]: insert_table: announce: id -1, l_user joy, r_user sakae, r_tty ttyp0
Mar 24 08:44:26 ob talkd[39953]: process_request: announce: success, id 1
Mar 24 08:44:44 ob talkd[39953]: process_request: delete: id 1, l_user joy, r_user sakae, r_tty
Mar 24 08:44:44 ob talkd[39953]: delete_invite(1)
Mar 24 08:44:44 ob talkd[39953]: delete: announce: id 1, l_user joy, r_user sakae, r_tty ttyp0
Mar 24 08:44:44 ob talkd[39953]: process_request: delete: success, id 0

check code

syslogの結果とコードをつき合わせてみる。忘れないうちに、目についた所を。

cc = recvfrom(STDIN_FILENO, (char *)&request,
    sizeof(request), 0, (struct sockaddr *)&response.addr,
    &len);
:    
cc = sendto(STDOUT_FILENO, (char *)&response,
    sizeof(response), 0, &ctl_addr, sizeof(ctl_addr));

メインのループの中で、パケットの送受部分。 規約では0だけで賄うという説明だったんだけど、、

ob$ cd /usr/include/
ob$ grep STDIN_FILENO * | grep define
unistd.h:#define        STDIN_FILENO    0       /* standard input file descriptor */

STDOUT_FILENO は、1 となってた。その方が、ずっと馴染があるからね。前言 撤回の、あの人みたい。

process.c

process_request(CTL_MSG *mp, CTL_RESPONSE *rp){
        if (debug)
                print_request("process_request", mp);
        switch (mp->type) {
        case ANNOUNCE:
        case LEAVE_INVITE:
        case LOOK_UP:
        case DELETE:
        default:
        if (debug)
                print_response("process_request", rp);

こんな風に、要求によってディスパッチしてる。入口と出口のパケットを、それ ぞれsyslogしてるんだけど、その表題が同名だ。まぎらわしいと思えるぞ。そ れとも、詳細内容が違うから、それで判別しろってか? ちょいとの工夫で、 もっと良くなると思うぞ。ちなみに、

print_request(char *cp, CTL_MSG *mp)
        syslog(LOG_DEBUG, "%s: %s: id %d, l_user %s, r_user %s, r_tty %s",
            cp, tp, mp->id_num, mp->l_name, mp->r_name, mp->r_tty);

print_response(char *cp, CTL_RESPONSE *rp)
        syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num));

こんな感じだ。

ひとつ追ってみる。process.c

case LEAVE_INVITE:
        ptr = find_request(mp);
        if (ptr != NULL) {
                rp->id_num = htonl(ptr->id_num);
                rp->answer = SUCCESS;
        } else
                insert_table(mp, rp);

招待する所。

table.c

find_request(CTL_MSG *request)
        if (debug)
                print_request("find_request", request);
        return (NULL);

insert_table(CTL_MSG *request, CTL_RESPONSE *response)
        if (debug)
                print_request( "insert_table", request );

テーブル(リンク・リスト)に登録されていないか確認して、未登録なら登録。 これ2重登録の防止を図っている。

struct table_entry {
        CTL_MSG request;
        time_t  time;
        TAILQ_ENTRY(table_entry) list;
};
TAILQ_HEAD(, table_entry)       table;

テーブルは、こんな風。登録時刻をエレメントに保持してるのは、メッセージ の繰り返しを防ぐため。有効時間が60秒に設定されてて、それを越えると、エ ントリーが削除される。

Noto

前回だったか、wiresharkを入れた時、メニューが豆腐だった。それで適当な フォントを入れて回避した。そんじゃ、ブラウザーの一つでも入れておこうと 思ったんだ。firefoxでもと入れようとしたら、i386はサポート外だった。 それじゃ、chrome でもとなった。無事に導入できた。

Notoなんて言うフォントを引き連れてきたぞ。なんじゃいこれ? 能登のロー カル・フォントかいな? さざ波とか東雲みたいに、ユニークな名前のフォントが多いですから。一応調 べておくか。

/ports/fonts/noto

Noto is a font family designed to cover the entire range of the Unicode
standard.

When text is rendered by a computer, sometimes characters are displayed as
"tofu". They are little boxes to indicate your device doesn't have a font to
display the text. Google has been developing a font family called Noto,
which aims to support all languages with a harmonious look and feel. Noto is
Google's answer to tofu. The name noto is to convey the idea that Google's
goal is to see "no more tofu". Noto has multiple styles and weights, and
freely available to all.

Googleの豆腐文字撲滅作戦の成果ですとな。納得しましたです。豆腐文字も国 際的になったものだな。

ChatGPT

Q: 光ファイバーが温度障害で、通信エラーを起こす事があるって話を聞きま した。本当ですか? 事例があったら教えてください。

A: 光ファイバーが温度によって通信エラーを起こす可能性はありますが、一般的には正しく設計された光ファイバーネットワークではそれが起こることは稀です。光ファイバーケーブルは一般的に温度変化に対して比較的安定していますが、極端な温度変化や不適切な設置などの要因によって問題が生じることがあります。

例えば、以下のような事例が報告されています:

  1. 極端な温度変化による影響: 光ファイバーケーブルが急激な温度変化にさらされると、光ファイバー自体や接続部分の物理的な変形が起こる可能性があります。これは光の伝送特性に影響を与え、通信エラーを引き起こす可能性があります。
  2. 熱膨張による問題: 温度変化によってケーブルや接続部分が膨張・収縮することがあり、これが光ファイバーの位置や方向を微妙に変化させることがあります。これにより、光の伝送経路が変わり、通信エラーが発生する可能性があります。
  3. 熱による信号劣化: 温度上昇によって光ファイバー内の材料が劣化することがあり、光の伝送特性が損なわれる場合があります。これにより、信号の品質が低下し、通信エラーが生じる可能性があります。

これらの問題を避けるためには、適切な光ファイバーの設計、適切な保護、適 切な温度管理が重要です。例えば、温度変化の激しい環境では、特に耐熱性の あるケーブルや適切な冷却装置を使用することが重要です。また、光ファイバー の設置や保守において、温度変化が問題となる可能性を考慮し、適切な対策を 講じることが重要です。

Q: 光の伝送特性と温度って関係あるのですか? 温度によって、伝送スピード が変わるとか、屈折率が変化するとかあるのでしょうか? 材料は石英ガラス ですよね。それとも最近は、プラスティック製が多いのかな?

A: 光ファイバーの伝送特性には、温度の影響があります。光ファイバーは一般的に石英ガラスから作られていますが、一部の応用ではプラスティック製の光ファイバーも使用されることがあります。

温度が変化すると、光ファイバー内の材料(主に石英ガラス)の屈折率が微妙に変化することがあります。この屈折率の変化は、光の伝送速度に影響を与える可能性があります。一般的に、温度が上昇すると光ファイバー内の材料の屈折率が低下し、光の速度が速くなる傾向があります。逆に、温度が下がると光の速度が遅くなる傾向があります。

このような温度による屈折率の変化は、光信号が光ファイバーを伝播する際に時間遅れや位相のずれを引き起こす可能性があります。特に高精度や高速の通信システムでは、温度変化が信号の品質に影響を与えることがあります。

プラスチック製の光ファイバーは一般的に石英ガラス製のものよりも柔軟性があり、特定のアプリケーションに適していますが、熱や温度変化には敏感です。そのため、プラスチック製の光ファイバーを使用する場合は、温度管理がより重要になります。

総じて言えることは、温度変化が光ファイバーの伝送特性に影響を与える可能 性があるため、設計や運用段階でこの点を考慮する必要があるということです。

Q: 光ファイバー温度計って、有りそうと思いますが、いかがでしょう?

A: 光ファイバー温度計の開発や利用は、光ファイバーネットワークの管理や保守において有益である可能性があります。光ファイバーネットワークにおける温度のモニタリングは、以下のような利点が考えられます:

  1. ネットワークの安定性向上: 温度変化が通信エラーを引き起こす可能性があるため、温度を定期的にモニタリングすることで、問題が発生する前に予防措置を講じることができます。これにより、ネットワークの安定性や信頼性が向上します。
  2. 故障の早期発見: 温度計を使用することで、異常な温度上昇や急激な温度変化を検出し、ポテンシャルな故障の早期発見につなげることができます。これにより、ネットワークのメンテナンスや修復を迅速に行うことが可能になります。
  3. 設備の効率化: 温度データを収集し、光ファイバーネットワークの設備の動作状況を評価することで、効率的な運用やリソースの最適化が可能になります。例えば、冷却装置の制御や設置場所の最適化などが考えられます。
  4. 品質管理の向上: 温度計を使用して温度データを記録・分析することで、ネットワークの品質管理を向上させることができます。温度に関連する問題を特定し、改善策を検討することで、ネットワーク全体のパフォーマンスを向上させることができます。

以上のような利点があるため、光ファイバーネットワークの運用管理において、 光ファイバー温度計の導入は有益であると考えられます。これにより、ネット ワークの安定性や信頼性の向上、故障の早期発見、設備の効率化、品質管理の 向上などのメリットが得られるでしょう。

質問者に、よいしょしてないか、自力で確認。

光ファイバー温度センシング

光ファイバー温度測定入門

本当に有った。ビックリですよ。


This year's Index

Home