BBS.pmの通信処理は一般的に行われるような方法とは違った方法で行います。
多少難解ですが、通信処理はサーバアプリケーションの要となる部分ですので、本章を熟読して理解を深めていただけると幸いです。
なおパッケージには通信処理をモジュール化するための具体例として入力制御モジュール(Input)と出力制御モジュール(Output)を同梱しています。
通信処理のモジュール化にチャレンジする場合はモジュールのドキュメントやモジュールのソースコードなども参照いただくとより理解を深められると思います。
ここでは、受信処理がBBS.pm(サーバシステム)とアプリケーションの双方でどのようなことが行われるのかを説明します。
受信処理の流れは下図の通りです。
BBS.pmはノードからの受信データを取得するとonRecvイベントを発生し、受信データを引数に設定してonRecvハンドラを呼び出します。
onRecvハンドラに受信データが渡るとBBS.pmの役目はここで終了します。
受け取った受信データはアプリケーションのデータとして活用するために、概ね以下の処理を行うことになります。
通信データはブロック(パケット)単位で伝送されます。
ノードからの送出されるデータ量は使用するtelnetクライアントの仕様によりますが、BBS.pmでは一度で取得できるデータサイズを4096バイトとしているため、ノードから送られるデータが4096バイトを超える場合は数回に分けて送出が行われるようデータが分割されます。
データを受信するとonRecvイベントが発生しますが、分割されたデータは分割データを受信するごとにonRecvイベントが発生します。そのため、分割されたデータを受信したときに呼び出されるonRecvハンドラから続きの分割データをつづけて取得することはできません。
続きの分割データはonRecvハンドラを抜けるとBBS.pmが続きの分割データを取得することでonRecvイベントが発生し、呼び出されるonRecvハンドラから取得することができます。
ここで注意すべきことは分割されたデータの分割点が文字コードの中間で分断していると文字データの断片は続きのデータを受け取って連結を行わないと文字データとして認識することができません。
たとえばUTF-8の文字コードは日本語や記号などは1文字あたり3~4バイトで構成していますが、1文字あたりの文字コードの1バイト目と2バイト目の境目、または2バイト目と3バイト目の境目などでデータが分断されているとデータを受け取った時点では文字として識別できません。
分断された断片データを文字として認識するためには、続きのデータを受け取って取得済みのデータと連結する必要がありますが、データの続きを取得するにはonRecvハンドラを抜け、次のonRecvハンドラが呼び出されないと取得できませんので、取得したデータをバッファリングしてonRecvハンドラを抜け、次のonRecvハンドラで続きの分割データを取得したときにバッファリングした分割データと結合します。
ここまでをまとめると次のようになります。
データを取得するとonRecvハンドラが呼び出されます。データサイズの大きいデータは分割されて取得するため、その都度onRecvハンドラが呼び出されます。 取得した分割データの場合、データの分割点が文字コードの中間であると文字コードの断片となり取得した時点では文字として識別できず、分断した文字コードの破片を文字コードとして識別するためには、取得している分割データと新たに取得する分割データとの連結(結合)が必要です。 分割データの続きを受信する場合は、処理中のonRecvハンドラを抜け、次のonRecvハンドラが呼び出されたときに取得することができます。 分割されたデータを受信したときに呼び出されるonRecvハンドラから続きの分割データを続けて取得することはできません。 分割データの結合を行う場合はonRecvハンドラを抜ける必要があるため、取得したデータのバッファリングを行う必要があります。 データを受信したとき、BBS.pmは取得した受信データの引き渡ししか行いません。
取得した受信データはこの時点では単なるバイナリデータにすぎず、そのデータがどのような意味をもっているかをアプリケーションが理解する必要があります。
ちなみにこのような識別処理は一般的にプロトコル処理(プロトコル解析)と呼ばれていてウェブサーバやメールサーバなど全てのアプリケーションサーバは各々のプロトコルに従って処理が行われることでクライアントからの要求を理解してサーバに対して適切に応答することができます。
プロトコル処理はアプリケーションによって異なるため、BBS.pmが事前に用意できるものではありません。従って、プロトコル処理はアプリケーションの一部として用意することになります。
受信データはプロトコル処理を施すことでアプリケーションが扱うことのできるデータとして得ることができるようになります。
受信データをアプリケーションのデータとして扱うためにはプロトコル処理を施す必要があります。 プロトコル処理とバッファリングは性質的にサーバ側で用意するものではないため、アプリケーションの一部として用意します。
プロトコル処理が施されたデータはパラメータやコマンド、フラグなどに変わりアプリケーション全体で活用されますが、そのためにはそれらのデータのバッファリングが必要があります。
バッファリングは難しいことではありませんがデータ量が大きい場合はディスクやデータベースなど、メモリ外に保存するような工夫が必要です。
ここまでをまとめると次のようになります。
受信データが大きい場合は分割して受信するため、データの分割点が文字コードの中間であると文字コードの断片となり、取得した時点では文字として識別できません。
分割データの続きは、処理しているonRecvハンドラを抜け、次のonRecvハンドラが呼び出されないと取得できません。
onRecvハンドラに引き渡された受信データをアプリケーションのデータとして扱うためにはプロトコル処理を施す必要があります。 プロトコル処理とバッファリングは性質的にサーバ側で用意するものではないため、アプリケーションの一部として用意します。 プロトコル処理を施したデータをシステム内で活用するためにはデータのバッファリングが必要になります。
以上3つの処理は、受信データを受け取るonRecvハンドラと例えばアプリケーション処理ハンドラ(AppWork)など、アプリケーションのデータとして取得や参照をが行われるハンドラのいずれかで行います。
受信処理の流れを示した図ではonRecvハンドラで受信データを取得し、そのままバッファリング、AppWorkハンドラなどでアプリケーションのデータとして取得、参照が行われるときにバッファリングした受信データを取り出し、プロトコル処理を施したのち、アプリケーション側に引き渡していますが、作成するサーバアプリケーションによってはこの方法が最適でないこともあるため、設計の際、十分考慮する必要があります。
ここでは、送信処理がBBS.pm(サーバシステム)とアプリケーションの双方でどのようなことが行われるのかを説明します。
受信処理の流れは下図の通りです。
データの送信を行うときは関数send()を呼び出しますが、この関数が呼び出されるとonSendイベントが発生し、送信データを引数に設定してonSendハンドラを呼び出します。
送信データは呼び出されたonSendハンドラに引き渡されますが、onSendハンドラではデータの送出を行うことはできません。
送信データの送出はデータ送出を行うためのOutputハンドラが呼び出され、このとき送出するデータを返り値に設定してOutputハンドラを抜けるとOutputハンドラの呼び出し元であるOutputイベントに渡ってその値が送出されます。
従って、onSendハンドラでは送信データがOutputハンドラで取得できるようにするためには送信データのバッファリングが必要です。
ここまでをまとめると、次のようになります。
データを送信するときBBS.pmは送信データの引き渡ししか行いません。 データ送出はOutputハンドラしか行うことができません。 onSendハンドラで受け取った送信データをOutputハンドラで取得できるようにするためにはデータのバッファリングが必要になります。
プロトコル処理は受信処理と違って行っていません。
その理由は処理の順序を考えると明らかですが、送出が行われるデータは既にデータ処理が施された完成形であるのが前提であり、不完全なデータを送出しようとは思わないはずです。
しかしプロトコル処理など何らかのデータ処理を関数send()の後で施したいとするなら、複雑なデータ処理は関数send()を呼び出す前までに行い、単純(単調)なデータ処理は関数send()の後で呼び出されるonSendハンドラで行うようにすれば実現できますが、作成するサーバアプリケーションの仕様によっては適切に処理できない可能性もあります。