<<Mind Ver.7 ドキュメント>>             −−−−−−−−−−−−−−−             ソケットライブラリ (socketlib)             −−−−−−−−−−−−−−−           ※ Copyright (C) 1995 Rigy Corporation ※           ※ Copyright (C) 2000 Scripts Lab. Inc. ※ ※ 改訂記録 ※ ※ 1997.11.30 新規作成(片桐) ※ 1998.08.24 Windows版のために修正。 ※ 2004.05.09 Ver.7.5対応 ※ 2017.03.08 Ver.8 暫定マニュアル利用のため一部訂正 ※ 改訂終り ※     本マニュアルは旧 Mind Version 7 向けのプレーンテキストの     マニュアルを一部訂正したもので暫定マニュアルとしてお使い     ください。     旧版特有の記述が一部残っていますが、正式マニュアル提供ま     での一時的なこととしてご容赦願います。 __________ 0.概要  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ Windowsには「Winsock」という著名なソケットAPI(標準DLLのひとつ)が装備され ており、これを使うことでTCP/IP通信を比較的簡単におこなえるとされています。  しかしAPIレベルでのプログラム開発は、多くのAPIを組み合わせることや、特殊な 構造体を用意するなど面倒なことがらに数多く付き合うことになります。  しかし本ライブラリを使えば、APIレベルの次元ではなく、より簡単で抽象化され たMind単語を利用できるようになります。これにより、他の言語での開発に比べ、ず っと簡単にソケットを使ったアプリケーションを組めるようになります。  たとえば、新しいメーラを開発したり、インターネット上に存在する二つのマシン の間でプロセス間通信をおこなうといった本格的な開発にも使えます。  ソケットAPIはすべて本ライブラリの中に隠蔽されており、ユーザが直接に関与する 必要は無いのですが、ソケットの概念を知ったり、あるいは用語を理解するために市 販の書籍を参考にされることをお勧めします。とりあえず二つの書籍を紹介します。 ----------------------------------------------------------------------  1.「UNIXネットワークベストプログラミング入門」雪田修一著、技術評論社    解説:UNIXと銘打っていますが、ソケットの概念はまったく同じです。       本ライブラリはAPIを裸で使うわけではないので、その意味でも UNIXとWindowsの違いは気にする必要はありません。分量が少なく       図も多くて読みやすいので初心者にはこちらを勧めます。  2.「インターネットのためのWinsockプログラミング」櫟易道著、技術評論社 (いちいやすみち) 解説:分量が多くしっかり書かれているので、本格的に学習したい場合       にお勧めです。ただし半分以上を占めるサンプルプログラムは、 C++のものです。 ---------------------------------------------------------------------- __________ 1. オペレーション  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ソケット関連のオペレーションマニュアルとしてHTML版の第6章をお読みください。 __________ 2. プログラミング  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ 2−1 ソケットとファイル  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ■FILEライブラリの処理単語について  「ソケット」は、ファイル廻りで言えば「ハンドル」とほぼ同じものです。接続が 終わってしまえば、あとは普通のファイルアクセスと思って差し支えありません。  一般的なファイルアクセスでは、物理ファイル名と論理ファイル名を与えて「オー プン」し、以後は論理ファイル名を指定して読み出し/書き込みをおこなっていまし た。  もちろん、ソケットを受け取ったあと、同様にして「継続オープン」などを使い 以降のアクセスを論理ファイルレベルでおこなうことは可能です。  しかし、簡単にソケットをアクセスできるよう(実はパイプにおいても同様のこ とが言えますが)、Version 7 以降では「ハンドル指定で・・」という系統の処理 単語を使うことで、ハンドルレベルのままデータの読み書きを行うことができるよ うになっています。  たとえば、     ○○構造体を ソケット1に ハンドル指定で書き込み     行データ1を ソケット1に ハンドル指定で一行書き込み のように送信したり、     ソケット1から データブロック1に ハンドル指定で構造体に読み出し     ソケット1から ハンドル指定で一行読み出し のようなものです。これらハンドル経由の処理単語はソケットライブラリではなくフ ァイルライブラリ管轄である点にご注意ください。これらの単語の解説は file.docm (F7. パイプ)をご覧願います。 ■ソケット番号に対するMind特有の介入  Windows版Mindでは、本ライブラリがソケット番号を負の値として扱っています。  ソケット値をモニタ表示した時などに負の値を見つけても驚かないでください。 (API発行の直前に正に戻しています)  WindowsではOSの仕組み上、ファイルのアクセスとソケットのそれが別になってい るのですが、Mindではライブラリ内でこれを統合しどちらであっても同じMind処理 単語で読み書きができるようにしています。  たとえば、ソケットに対しても、ファイルに対しても「ハンドル指定で書き込み」 や「ハンドル指定で読み出し」「ハンドル指定でクローズ」など同じ処理単語を使え るようにするためです。    (注:UNIX版では両者はOSレベルで同一扱いであるためこのような       仕掛けはしていません) ■行データとブロックデータ  ここで言う「行データ」とは、長さが不定長で、末尾に改行コードの入ったような データです。「ブロックデータ」とは、たとえば構造体のように長さが固定されたデ ータです。  ソケットを経由してこのどちらのタイプのデータも送受できますが、最後のところ はパケットでやりとりするので、本当はブロックデータのやりとりのほうが向いてい ます。  行指向のデータは、サンプルプログラム紹介の「相互通信のプログラム(server/cl ient)」でのデータのように、簡単な(1回の送受が1行に収まる)ものに適用して ください。  なお、行データをブロックデータ化することは可能です。文字列実体変数を一つだ け抱えるような構造体を定義し、その構造体を送受すれば良いのです。サンプルプロ グラム紹介にあった「チャットプログラム(chat-server, chat-client)」では、あえ て複雑にして構造体経由で送受しているので参考にしてください。(構造体の定義は 小さな副ソースファイル"commdata.src"の中にあります) ■パケットの意識について  ソケットの概念上ではパケットが隠蔽されていますが、一部、パケットであるこ とを意識する必要が生じる場合もあります。その多くは「行単位」でデータを扱う 場合です。  たとえば、   (送信側)     「このような」を ソケット1に ハンドル指定で書き込み     「文字列」を ソケット1に ハンドル指定で一行書き込み   ↑↑   (受信側) ソケット1から ハンドル指定で一行読み出し  のようなプログラムの場合、送信したつもりの「このような文字列」という文字列 を受信できないケースがありますから注意してください。二つのパケットに分けられ てしまうと正しく受信できないことがあります。(注:「ハンドル指定で一行読み出 し」はワンパケット受信した段階で(正確には、1回のread()で)リターンしてしま います(たとえ行末コードを検出しなくても))  前記ケースでは、   (送信側)     「前半」と 「後半」を 合成し ソケット1に ハンドル指定で一行書き込み   (受信側) ソケット1から ハンドル指定で一行読み出し  のようにすれば解決します。file.docm でも解説されていますが、「ハンドル指定 で一行書き込み」は内部にバッファを持ち、それを使って改行コードを付加し、1回 のAPI発行で書き込むようにしています。 注:Version 7 でのFILEライブラリにある読み出し系の単語は、       ブロック読み出し系(「読み出し」など)と、行指向の読み   出し系(「一文字読み出し」「一行読み出し」など)とで       は内部処理が異なります。       前者では、バッファが満たされるまでしつこくread()を繰り       返します。大きなデータが複数のパケットに分割されて到達       することがあるからです。       後者では1回のread()で済ませます。前記したような行単位       のデータでは2度read()をおこなうと、処理が停止してしま       うことが多いからです。この理由により、「ハンドル指定で       一行読み出し」は、パケットサイズ(たとえば4096バイト)       を越えたデータを扱うことができません。 ■タイムアウト処理と論理ファイルとの関連付け (Ver.7.5以降)  Mindにおいてはソケットハンドルはファイルハンドルと同等であるため、ハンドル レベルでのアクセスのほか、ファイルとしてアクセスすることもできます。特に、 以下に示すタイムアウト処理をおこなうには、ファイルとしてのアクセス形態が必須 となります。 □論理ファイルとの関連付け (Ver.7.5以降)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <ソケット>で <論理ファイル>を 継続オープンし → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  上記記述により、指定したソケットを指定した論理ファイルに関連付けることが できます。この後、ソケットのアクセスはファイルアクセスのように行うことがで きます。たとえば「一行読み出し」が使えます。  なおクローズ時は、普通のファイルと同様に【<論理ファイルを> クローズ】 を使ってください。さらにその後で、ソケットのクローズをおこなってください。 □タイムアウト指定 (Ver.7.5以降)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <待ち秒数>を <論理ファイル>に ソケット待ち秒数設定 → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  ソケットを関連付けした論理ファイルに対して、待ち時間を指定します。単位は 秒です。これを指定すると、それ以降の読み出し系のファイル読み出しの処理単語 を実行した場合、タイムアウト処理がおこなわれます。すなわち、無限に待つので はなく、一定時間経過した場合にはエラーリターンさせることができます。   (プログラム例)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     データソケットは 変数。     ソケット読み出しファイルは ファイル。     ソケットをファイルに関連付けとは (・ → ・)         データソケットで ソケット読み出しファイルを 継続オープンし         エラー?             ならば 終り             つぎに         30秒をを ソケット読み出しファイルに ソケット待ち秒数設定し         。     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− □読み出し時のエラー処理 (Ver.7.5以降)  ソケットが関連付けられたファイルから「一行読み出し」をおこなった場合、 「データ終り?」だけでなく「エラー?」も必ず検査するようにしてください。  おおむね、コネクションが切れた場合には「データ終り?」が真を返すよう になり、タイムアウト時には「エラー?」がエラーを返すようになります。 2−2 処理単語解説(ソケット)  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ■IPアドレスを文字列変換     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <数値>を            IPアドレスを文字列変換 → 文字列     <数値> <文字列実体>をつかい IPアドレスを文字列変換 → 文字列     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  数値としてのIPアドレスを、"n.n.n.n"形式で表現される、文字列としてのIPアド レスに変換します。 ■IPアドレスを表示     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <数値>  IPアドレスを表示 → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  数値としてのIPアドレスを、"n.n.n.n"形式でコンソールに表示します。 ■ホスト名をIPアドレスに変換     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ホスト名(文字列> ホスト名をIPアドレスに変換 →            IPアドレス(数値32)、合否     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  "host.domain" の形式または、"n.n.n.n" の形式のホスト名をIPアドレスに 変換します。   〜以下は上の下位単語です〜   □名前表現ホスト名をIPアドレスに変換     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ホスト名(文字列> ホスト名をIPアドレスに変換 →            IPアドレス(数値32)、合否     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−    "host.domain"形式で指定したホスト名をIPアドレス(数値)に変換します。    内部的では gethostbyname() を使っています。 すなわち、処理単語の名前とは   うらはらに、ネットワークアクセスが発生します。   □数値表現ホスト名をIPアドレスに変換     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ホスト名(文字列> 数値表現ホスト名をIPアドレスに変換 →            IPアドレス(数値32)、合否     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−    "n.n.n.n"形式で指定したホスト名をIPアドレス(数値)に変換します。    gethostbyname() は呼ばれず、単なる文字列変換機能になります。 ■数値表現ホスト名? (Ver.7.5以降)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <ホスト名> 数値表現ホスト名? → 真偽     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  指定したホスト名が n.n.n.n 形式の直接的な数値表現形式であるかを検査します。  名前表現か数値表現か(Yes/No)で処理を分岐させたいケースで使うことを想定して いるため文法チェックは緩いです。 ■数値表現ホスト名?{辛い} (Ver.7.5以降)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <ホスト名> 数値表現ホスト名?{辛い} → 真偽     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  前記と同様ですが、こちらは厳格な検査を行います。本当に正しい数値表現かど うかを見ます。 ■listen     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <ホスト名> <ポート番号> <キューサイズ> listen        → LISTENソケット     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  内部では次のことが行われます。   (1) ホスト名をIPアドレスに変換。名前で書かれていた場合には gethostbyname()を実行 (2) socket() を実行(パラメータは [AF_INET, SOCK_STREAM, 0] です) (3) bind() を実行 (4) listen() を実行(ここでキューサイズが使われます)  エラーリターンが有ります。戻ったら必ずエラーを調べてください。 ■accept     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <LISTENソケット> accept → ソケット     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  内部で accept() を実行します。これで得られたソケットで以後のデータの読み書 きを行います。  エラーリターンが有ります。戻ったら必ずエラーを調べてください。 ■connect     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     <ホスト名> <ポート番号> connect → ソケット     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  内部では次のことが行われます。   (1) ホスト名をIPアドレスに変換。名前で書かれていた場合には gethostbyname()を実行 (2) socket() を実行(パラメータは [AF_INET, SOCK_STREAM, 0] です) (3) connect() を実行  エラーリターンが有ります。戻ったら必ずエラーを調べてください。     注:Version 8 からは、非同期のconnectが使えます ■ハーフクローズ     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ソケット> ハーフクローズ → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  指定するソケットをハーフクローズ(書き込み機能のみクローズ)します。  まだ受け取るデータが残っている状態であるにもかかわらず、相手ポートに対し 全データを送り付けたい(相手をEOF状態にしたい)場合に使います。 注:普通のソケットのクローズは「ハンドル指定でクローズ」を       使ってください。file.docmの管轄なのでここでは解説しません。 2−3 処理単語解説(select)  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  旧版のMindには「キー入力有り?」という処理単語がありました。これを使うと、 ふだんは別の処理をしながら、時々キー入力が有るかを検査し、有った時だけキー 入力に対するサービスを行う・・といった擬似的な並行処理をおこなうことができ ました。  Version 7 ではこの「キー入力有り?」が消滅していますが、その代わりに、 より強力で汎用の機能が提供されます。純粋にはソケット通信と独立したものなの ですが、ソケットでよく使われるのでこのライブラリに含めています。  以下に必要な処理単語を解説していますが、実際の使用例として、サンプルプロ グラムの章で取り上げた、「チャットプログラム(chat-server, chat-client)」の ソースをぜひお読みください。このサンプルでは、キー入力が有れば送信する、 一方で、受信データがあればコンソールに表示するといった並行動作をおこなうた め、これを使っています。  なお、「キー入力有り?」に相当することをさせるには、ハンドル=0、すなわ ち標準入力ハンドルを以下の処理に適用してください。 −−− (入力検査) ■入力検査情報型 (型紙)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     (利用例) 入力検査情報1は 構造体 入力検査型   (520バイトを消費)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 後述する入力検査関係の処理単語で使います。  デフォルトの入力検査情報を流用する使い方であればこの定義は不要です。     注:これはAPI側での fd_set の構造体獲得に相当するものですが、       都合により、Mind側ではサイズを大きめな決めうちとして上記       の大きさにしています。ソケットライブラリ立ち上がり時に       万が一、sizeof( fd_set ) がこれを越えている場合には、 --------------------------------------- struct fd_set の大きさ(nn)がMind側バッ ファ(mm)を越えています。 --------------------------------------- というメッセージを出力してアプリケーションが強制終了しま す。この場合には恐れ入りますが、 ---------------------------------------         入力検査情報型は 型紙   入力検査フラグは  データ領域 長さ 512 ---------------------------------------       の数字を大きくして socketlib を再コンパイルしてください。     注:この構造体は比較的サイズが大きいため、できれば(局所では       なく)大域変数として定義してください。 ■入力検査フラグ初期化     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−           入力検査フラグ初期化する → ・ <入力検査情報>を 入力検査フラグ初期化する → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  入力検査の最初に行います。  入力検査情報が省略された場合にはデフォルトの情報が使われます。普通は省略し て構わないでしょう。  APIの、FD_ZERO() に対応します。 ■入力検査フラグをセット     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ハンドル>を           入力検査フラグをセット → ・ <ハンドル>と <入力検査情報>で 入力検査フラグをセット → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  調べたいハンドルをこれで通知します。「入力検査フラグ初期化」のあとで実行し てください。  入力検査情報が省略された場合にはデフォルトの情報が使われます。普通は省略し て構わないでしょう。  APIの、FD_SET() に対応します。 ■入力検査     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <待ち時間>で           入力検査 → ハンドル数 <待ち時間>と <入力検査情報>で 入力検査 → ハンドル数     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  入力可能なデータ(ハンドル)があるかを調べます。戻りデータは入力可能を検出 したハンドルの数です。これが真なら何か入力可能なものがあるという意味になりま す。後続して「入力可能?」を呼んでください。  待ち時間は秒単位です。  ゼロを指定すると、調べて直ちにリターンします。  秒数が指定された場合には、どれかのハンドルが入力可能になるまで待ってから リターンします。負の値を指定すると無限に待ちます。  入力検査情報が省略された場合にはデフォルトの情報が使われます。普通は省略し て構わないでしょう。  APIの、select() に対応します。 ■入力可能?     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ハンドル>が           入力検査可能? → 真偽 <ハンドル>と <入力検査情報>で 入力検査可能? → 真偽     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  「入力検査」に引き続き実行するもので、指定したハンドルが入力可能であるかを 検査します。  入力検査情報が省略された場合にはデフォルトの情報が使われます。普通は省略し て構わないでしょう。  APIの、FD_ISSET() に対応します。 (入力検査の簡単なサンプル) ----------------------------------------------------------------------------- スキャンとは (・ → ・) 「スキャン:」を 表示し 4を 回数指定し 回数−1が 入力可能?を 数値表示し 空白表示し 繰り返し 改行し。 メインとは 「入力検査フラグ初期化状態」を 一行表示し 入力検査フラグ初期化し スキャンし 改行し 「SELECTのループに入る・・」を 一行表示し 「(2度目以降のループで何かの文字とEnterを入力してみてください)」を 一行表示し 改行し ここから 入力検査フラグ初期化し 0で 入力検査フラグをセットし 「5秒を限度に入力を待つ・・」を 表示し 5秒で 入力検査し 「結果=」を 表示し 数値表示し 改行し 0が 入力可能? ならば 打ち切り つぎに 繰り返し 「標準入力からデータが有ったことを検出しました。」を 一行表示し スキャンし 文字列入力し 「入力データ=」を 表示してから 一行表示し 「終り時点」で このメッセージでスタック検査し。 ----------------------------------------------------------------------------- −−− (出力検査)  入力検査とまったく対称な、出力検査の機能を用意しています。使用頻度はそれ ほど高くないと思われるので、列挙のみにとどめます。 ■出力検査情報型 (型紙)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−     (利用例) 出力検査情報1は 構造体 出力検査型   (520バイトを消費)     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ■出力検査フラグ初期化     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−           出力検査フラグ初期化する → ・ <出力検査情報>を 出力検査フラグ初期化する → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ■出力検査フラグをセット     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ハンドル>を           出力検査フラグをセット → ・ <ハンドル>と <出力検査情報>で 出力検査フラグをセット → ・     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ■出力検査     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <待ち時間>で           出力検査 → ハンドル数 <待ち時間>と <出力検査情報>で 出力検査 → ハンドル数     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ■出力可能?     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− <ハンドル>が           出力検査可能? → 真偽 <ハンドル>と <出力検査情報>で 出力検査可能? → 真偽     −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ※ 終り(socketlib.docm) ※