1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

日本語プログラミング言語Mindの小技 「accept」~ソケット1対多の接続待機ループ~

Posted at

はじめに

日本語プログラミング言語Mindの小技「accept」について説明したいと思います。

対象読者

日本語プログラミング言語Mindのユーザー、または日本語プログラミング言語に興味のある方

この小技に関連するMind言語マニュアル

この小技に関連するMind言語仕様の記述はMind8プログラミングマニュアルに記載はありません。

Mind7の付属の上級者向けドキュメントmind7\doc\socketlib.docmに記載があります。

2-2 処理単語解説(ソケット)
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄

■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() を実行
 エラーリターンが有ります。戻ったら必ずエラーを調べてください。

記事筆者注 以下の引用からは入力検査情報型を指定した構文例および省略可能ですの説明文は割愛しています。

2-3 処理単語解説(select)
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄

■入力可能?
 --------------------------------
<ハンドル>が           入力可能? → 真偽
 --------------------------------
 「入力検査」に引き続き実行するもので、指定したハンドルが入力可能であるかを検査します。
 APIの、FD_ISSET() に対応します。

本機能(本記事)は、下記のバージョンに対応しています。Mind8のLinux版も対応していると思いますが、本記事では特に検証を行っておりません。

対応バージョン

■Mind7 ■Mind8 ■Mind9
■Windows版 □Linux版

小技の解説

Mindの小技「accept」は、Mindのソケット通信ライブラリ機能でコネクションを確立する機能の1つです。これに関連した単語は下記の6つとなります。
「listen」「accept」「connect」
「入力可能?」「入力検査フラグ初期化」「入力検査フラグをセット」「入力検査」

上段3つの単語はこちらの記事
下段4つの単語はこちらの記事にも詳しい解説があります。

「listen」と「accept」はサーバー側で使用します。
「connect」はクライアント側で使用します。

サーバー側はホスト名とポート番号とキューサイズで「listen」を実行すると、リッスン用ソケットがスタックに積まれますので、クライアントからの接続要求(「connect」を実行)をリッスン用ソケットで「accept」します。接続が成立すれば通信用ソケットをスタックにつみますので、それを使ってデータを読み書きします。

サーバーがリッスン用ソケットで「accept」を実行しているとき、クライアントからの接続要求があるまで待機しますので、この処理を受信ループにいれてしまうと、次のクライアントからの接続要求があるまで待ち続けます。

したがって、他のクライアントからのデータ送信要求を処理する際にループ処理が止まってしまうことになります。

データの送受信ループでは、該当ソケットが「入力可能?」かどうか、(その手前ではすべてのソケットから受信データのあるソケット数を返す「入力検査」を実行して0ならループ先頭に戻して無駄な処理を行わないようにして)チェックを行い、受信があった場合だけ実際のデータ受信処理に進行させることで、ループを止めないようにしています。

従来のソケット通信サンプルの実装では、このため所定のクライアントの接続が完了するまで、データ受信ループには進行させていませんでした。

この記事の直前に投稿されている下記の記事

Mindの小技 「ハンドルとサイズ指定で構造体に読み出し」~ソケット1対多の双方向通信~

のサンプルもそのような実装でしたが、この記事へのMind言語開発者の@killyさんからのコメントで、リッスンソケットも同じソケットなので「入力可能?」に関係する単語群で、データ受信ありのチェックと同様に処理を止めずに待ち受けをチェックできるかもとのサジェストをいただき、それを検証したのが本記事となります。

本記事では、1対多の双方向ソケット通信アプリケーションを実装しています。このとき、サーバーがクライアントからの接続を待ち受けますが、これを接続済のクライアントからのデータ送信を待ち受けるループ内で行えるようにすることがお題です。

ソケット通信の概念をつかみやすくるため、ホスト名やポート番号は固定書きの超シンプル版です。

Mindプログラムソース

サーバー側

socketsrv8.src
サンプルホスト名は   文字列定数 "localhost"。
サンプルポート番号は  数値    1000。
サンプルキューサイズは 数値    10。
通信データは 構造体
	通信文字列は 文字列実体 長さ 128
  全体は 通信文字列。
ソケット群は             10の 変数。

空いているソケット群の添え字を返すとは (・ → 空いているソケットの添え字 空きがなければ0を返す)
        ソケット群添え字は 変数

    ソケット群添え字を クリアし
    ソケット群の 要素数で 回数指定し
        ソケット群(回数)が 0に 等しい
        ならば
         回数を ソケット群添え字に 入れ 打ち切り
        つぎに
    繰り返す
    ソケット群添え字をかえす。

サーバ側で接続待機とは (ホスト名、ポート番号、キューサイズ → リッスン用ソケット)
     リッスン用ソケットは 		    変数

    (ホスト名と ポート番号と キューサイズで)
  listenし リッスン用ソケットに 入れ
	エラー?
    ならば エラー文字列を 一行表示し 実行終わり
	つぎに
    リッスン用ソケットをかえす。


コネクション確立とは (リッスン用ソケット → ソケット)
     ソケットは 		    変数

    (リッスン用ソケットで)
	acceptし (→ 別のソケット) ソケットに 入れ
    ソケットをかえす。

使用中ソケットの入力検査フラグをセットするとは (・ → ・)

    ソケット群の 要素数で 回数指定し
        ソケット群(回数)が 0に 等しい
        でなければ
         ソケット群(回数)で 入力検査フラグをセットし (受信データ有り)
        つぎに
    繰り返す。

使用中ソケットのデータ送信するとは (・ → ・)

    ソケット群の 要素数で 回数指定し
        ソケット群(回数)が 偽?
        でなければ
            通信データを ソケット群(回数)に ハンドル指定で書き込み
        つぎに
    繰り返す。

使用中ソケットのデータ送受信するとは (・ → ・)

    ソケット群の 要素数で 回数指定し
        ソケット群(回数)が 偽?
        でなければ
            ソケット群(回数)で 入力可能?
            ならば
                ソケット群(回数)と -1と 通信データへ ハンドルとサイズ指定で構造体に読み出し
                読み出しバイト数が ゼロ?
                ならば  
                        「クライアントの終了を検知しました。」を 一行表示し
                        ソケット群(回数)を ハンドル指定でクローズし
                        ソケット群(回数)を クリアし
                さもなければ
                    「 受信データ=」を 表示し 通信文字列を 一行表示し
                    使用中ソケットのデータ送信し
                つぎに
            つぎに
        つぎに
    繰り返す。


サーバー側ソケット通信とは (リッスン用ソケット → ・)
     リッスン用ソケットは 		    変数
        ソケット群添え字は 変数

    リッスン用ソケットに 入れ
    ここから
        入力検査フラグ初期化し
        リッスン用ソケットで  入力検査フラグをセットし
		使用中ソケットの入力検査フラグをセットし

		0秒で 入力検査し 偽?
        ならば もう一度
        つぎに
     (どれかデータ有り)
        リッスン用ソケットで 入力可能? 
        ならば
            空いているソケット群の添え字を返えし ソケット群添え字に 入れ
            ソケット群添え字が 偽?
            ならば
                「最大接続数に達しているためクライアントからの接続を無視しました。」を  一行表示し
            さもなければ
                リッスン用ソケットで コネクション確立し ソケット群(ソケット群添え字)に 入れ
                「クライアントとの接続を確立しました。」を  一行表示し
            つぎに
        つぎに
        
        使用中ソケットのデータ送受信し
    繰り返し。


メインとは (・ → ・)
         リッスン用ソケットは  変数

    「ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。」を
        一行表示し
    サンプルホスト名と サンプルポート番号と サンプルキューサイズで 
            サーバ側で接続待機し リッスン用ソケットに 入れ

    リッスン用ソケットで サーバー側ソケット通信し

	リッスン用ソケットを ソケットクローズし
  「ソケット通信サーバー側を終了しました。」を  一行表示すること。

ソケット数(最大接続数)は10としています。リッスンソケットをオープンしたら、受信ループに進行します。リッスンソケットのクローズは受信ループ離脱後となります。クライアントの接続要求の検知を受信ループ内の「リッスン用ソケットで 入力可能?」で行っています。

接続確立後のデータ受信や、ソケット検査の初期化は、ソケット配列でのループとしていますので、別処理単語として受信ループ内の記述をすっきりさせています。

クライアント側

socketclt5.src
サンプルホスト名は 文字列定数 "localhost"。
サンプルポート番号は 数値   1000。
標準入力ハンドルは  数値   0。
通信データは 構造体
	通信文字列は 文字列実体 長さ 128
  全体は 通信文字列。

コネクション確立とは (ホスト名、ポート番号 → ソケット)
     ソケットは 		    変数

    (ホスト名と ポート番号で)
	connectし ソケットに 入れ
    エラー?
    ならば エラー文字列で 重大エラー
    つぎに
    ソケットをかえす。


クライアント側ソケット通信とは (ソケット → ・)
     ソケットは 		    変数

    ソケットに 入れ
    ここから
		入力検査フラグ初期化し
        標準入力ハンドルで 入力検査フラグをセットし (入力データ有り)
        ソケットで     入力検査フラグをセットし (受信データ有り)

		0秒で 入力検査し 偽? (ハンドル数が0)
        ならば もう一度
        つぎに

		標準入力ハンドルが 入力可能?
		ならば
            文字列入力し 通信文字列に 入れ
            通信文字列が 空列?
            ならば 打ち切り
            つぎに
            通信データを ソケットに ハンドル指定で書き込みする
        つぎに

        ソケットで 入力可能?
        ならば
            ソケットと -1と 通信データへ ハンドルとサイズ指定で構造体に読み出し
            データ終り? または 読み出しバイト数が ゼロ?
            ならば 「サーバーの終了を検知しました。」を 一行表示し
                  打ち切り
            つぎに
            「 受信データ=」を 表示し 通信文字列を 一行表示し
        つぎに
        
	繰り返し。

メインとは (・ → ・)
         ソケットは 		    変数

    サンプルホスト名と サンプルポート番号で 
             コネクション確立し ソケットに 入れ
    「ソケット通信クライアント側を起動しました。」を
        一行表示し
    ソケットで クライアント側ソケット通信し
	ソケットを ハンドル指定でクローズし
  「ソケット通信クライアント側を終了しました。」を  一行表示すること。

クライアントはこちらの記事こちらの記事と同じです。

コンパイル結果

ではコンパイルしてみます。下位ライブラリはsocketlibを指定します。

Mind9

下図はMind9βです。

c:\developments\vscode\mind9>mind socketsrv8 socketlib       

日本語プログラミング言語 Mind Version 8.11 for Windows
          Copyright(C) 1985 Scripts Lab. Inc.
コンパイル中 .. 終了
Coping.. c:\mind9-beta\mind9-beta\bin\mindex.exe --> socketsrv8.exe

c:\developments\vscode\mind9>mind socketclt5 socketlib 

日本語プログラミング言語 Mind Version 8.11 for Windows
          Copyright(C) 1985 Scripts Lab. Inc.
コンパイル中 .. 終了
Coping.. c:\mind9-beta\mind9-beta\bin\mindex.exe --> socketclt5.exe

Mind8

c:\developments\vscode\mind9>mind socketsrv8 socketlib

日本語プログラミング言語 Mind Version 8.07 for Windows
          Copyright(C) 1985 Scripts Lab. Inc.
コンパイル中 .. 終了
Coping.. c:\pmind\bin\mindex.exe --> socketsrv8.exe

c:\developments\vscode\mind9>mind socketclt5 socketlib 

日本語プログラミング言語 Mind Version 8.07 for Windows
          Copyright(C) 1985 Scripts Lab. Inc.
コンパイル中 .. 終了
Coping.. c:\pmind\bin\mindex.exe --> socketclt5.exe

Mind7

C:\developments\vscode\mind9>mind socketsrv8 socketlib
日本語プログラミング言語 Mind Version 7.5 for Windows
          Copyright(C) 1985-2004 Scripts Lab. Inc.
          Single user license.  Serial No:********
コンパイル中 - 終了
Coping.. C:\mind7\bin\mindexec.exe -> socketsrv8.exe

C:\developments\vscode\mind9>mind socketclt3 socketlib
日本語プログラミング言語 Mind Version 7.5 for Windows
          Copyright(C) 1985-2004 Scripts Lab. Inc.
          Single user license.  Serial No:********
コンパイル中 - 終了
Coping.. C:\mind7\bin\mindexec.exe -> socketclt5.exe

実行結果

つづいて実行してみます。Mind8の結果ですが7/9βも同様です。

コマンドプロンプトターミナルは4つ起動します。
サーバー用1つ、クライアント用3つです。
※注意 お手数ですが、それぞれのターミナルでビルドしたMindのバージョンのランタイムパスをとおしておいてください。

最初にサーバーを起動します。

c:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。

これは起動しただけで、「リッスンやアクセプト」はまだ実行していません。
つづいてクライアント側を起動します。クライアントは3つのターミナルでそれぞれ起動します。3つ起動する分にはこの順序は任意です(^-^;。ただし、今回は1つ起動したらデータの送信もすぐ始めますので、連続して起動はしません。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。

サーバーは最初のクライアントの接続を検知して受付ます。

c:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。
クライアントとの接続を確立しました。

クライアントからデータを送信します。文字列をタイプしてEnterキーで送信です。サーバから返信があります。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。
こんにちは。クライアント1です。
 受信データ=こんにちは。クライアント1です。
 

サーバー側もクライアント1からのデータを受信しています。

c:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント1です。

つづいて、2つ目のクライアントを起動します。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。

サーバー側では下記のように接続確立のメッセージが表示されます。

C:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント1です。
クライアントとの接続を確立しました。

最後に、3つ目のクライアントを起動して、メッセージを送信します。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。
こんにちは。クライアント3です。
 受信データ=こんにちは。クライアント3です。

サーバー側では下記のように接続確立のメッセージと受信メッセージが表示されます。

C:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント1です。
クライアントとの接続を確立しました。
 受信データ=こんにちは、クライアント2です。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント3です。

いかがでしょうか?1対Nのソケット通信で、リッスン開始の後、アクセプト・コネクション確立、ソケットデータ通信がひとつのループ内で処理されているイメージがつかめれば幸いです。

ちなみに現状のバージョン同士のサーバとクライアントの場合、あるクライアントが送信したデータを別のクライアントが受信するタイミングが、クライアントの待機ループでリアルタイムに処理できなくなっており、クライアントがなにかを送信したタイミングで受信されるようです。このあたりは別の機会に深堀りしてみたいと思います。

クライアント1がさよならメッセージを送信した後、その受信データはクライアント2、3のこんにちはメッセージといっしょに受信しました。その後は、クライアント2,3のさよならメッセージを受信。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。
こんにちは。クライアント1です。
 受信データ=こんにちは。クライアント1です。
さようなら
 受信データ=こんにちは、クライアント2です。
 受信データ=こんにちは。クライアント3です。
 受信データ=さようなら
 受信データ=さようなら
 受信データ=good bye!

クライアント2がさよならメッセージを送信した後、その受信データはクライアント3のこんにちはメッセージといっしょに受信しました。その後は、クライアント1,3のさよならメッセージを受信。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。
こんにちは、クライアント2です。
 受信データ=こんにちは、クライアント2です。
さようなら
 受信データ=こんにちは。クライアント3です。
 受信データ=さようなら
 受信データ=さようなら
 受信データ=good bye!

クライアント3がさよならメッセージを送信した後、その受信データはクライアント1,2のさよならメッセージといっしょに受信。

C:\developments\vscode\mind9>socketclt5
ソケット通信クライアント側を起動しました。
こんにちは。クライアント3です。
 受信データ=こんにちは。クライアント3です。
good bye!
 受信データ=さようなら
 受信データ=さようなら
 受信データ=good bye!

サーバー側の受信状態です。

C:\developments\vscode\mind9>socketsrv8
ソケット通信サーバー側を起動しました。クライアントからの接続を待機しています。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント1です。
クライアントとの接続を確立しました。
 受信データ=こんにちは、クライアント2です。
クライアントとの接続を確立しました。
 受信データ=こんにちは。クライアント3です。
 受信データ=さようなら
 受信データ=さようなら
 受信データ=good bye!

参考情報

この小技「accept」を使った記述例の記事は下記が該当します。

おわりに

いかがでしたでしょうか?なにかの参考になれば幸いです。2025年は日本語プログラミング言語Mind生誕40周年です。

本記事シリーズのご紹介

本記事シリーズ「日本語プログラミング言語Mindの小技」は「日本語プログラミング言語Mind生誕40周年プロジェクト」の一環です。

興味を持たれた方は日本語プログラミング言語Mind公式サイトにアクセスすると、Mindコンパイラをダウンロードできますよ。

面白い!、楽しい、カンタン、難しいのも書ける!みんなでやってみよう:relaxed:

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?