ティアのたわごと3☆其の321 |
多忙のようで多忙でないようで・・・ |
---|
たわごとは実に10日ぶりになってしまいましたが、じつは、ページの更新自体も結構滞ってたりして・・・
先週の金土と、今週の日曜日は、限界まで遊ばせていただきまして、月曜日は・・・・そう、月曜日は一日デバッグのし続け、火曜日はネットワークの設定およびデバッグの続き
水曜日も木曜日も・・・・
金曜日は秋葉行ったり知り合いにあったりもしたけど、でもデバッグの続きで・・・・
とりあえず、終わったんですよ(笑
デバッグついでにメールサーバーまで調整させられて、大変だったんですよ(笑
TEA.mil☆ミちゃん作成のFlora.ne.jpのメールサーバー、実はたまにエラーがでるので何かと思ったのですが、間違いがありました(笑
インターネットとの通信は何時終わるか分からないので、通信が完了するまで結構じっくり待たないとダメです。でも、その最中画面が更新されないとか、ボタンも押せないとか・・・いろいろ問題が多いんですよね。
なので、マルチスレッドで組むか、WinSockの非同期通信を選択しないといけません。
この非同期通信は,
通信が完了するとウィンドウにメッセージを送ってくれるのですが、そもそもウィンドウが無いとダメとか、随時届くデータを随時送ってくれるので、ある大きさのデータをまとめて欲しいとか、ハンドシェイク(必ず応答を待つ)とかは非常に不便。
なので、これらを一括してやってくれるダブルバッファリング機能が有効で便利になります。
データ<->Winsockのバッファ<->ダブル(2nd)バッファ<->アプリケーション
アプリケーション側からはダブルバッファにたいしてアクセスし、ダブルバッファーはWinsockからいただいたデータを蓄積し、意味あるサイズにまとまった段階で、アプリケーションにデータを返すことで、余分な処理がいらなくなります。
さて、このとき重要なのが、Queue(キュー)って機能です。
(今回ダブルバッファリングの技術についてはこれ以上追求しません)
今回の主役はこのQueue
キューは、入ってきたデータをため込んで、入ってきた順番に吐き出すものです。
いわゆるデータの緩衝器みたいなものですが、バッファーとは広義のキューのことです。
さて、このQueueですが、入ってきたデータの順番が重要なので、データを順番に並べるのですが、問題は、Queueにたまったデータを取り出し中に、更にデータが入ってきたときです。
もちろん、同期(不整合を防ぐための手段)の事ではありません。
Queueをフルに活用するには、データが取り出されたら、その分前へ詰めてQueueに十分データがたまるようにしなくてはいけません。データは随時届いているのですから、重要です。
queueが十分小さいときや、たまっているデータが少ない時はあまり問題ありませんが、大きなデータがたまったときに前から少しずつ取られるたびに、queueの中身を先頭から並び直すのは非常に時間が掛かります。
なので考えられたのが、リングバッファです。
queueの先頭位置を動かすことで先頭から並べ直すのを省略できます。
でも、Queueはメモリ上に物理配置を持ち、先頭を動かしたからと行って、queue全体の場所は変更できません。なので、queueの最後まで行ったら、全体の先頭から書き始めます。
仮想上、queueは末尾と先頭が繋がりリング状になるのでリングバッファと呼ばれます。
さて、このとき注意が必要なのは、1周すると以前書いたデータの領域に舞い戻ってくると言うことです。
リングバッファはメモリアクセス数を下げるために作られた方法なので、当然データのクリアなんてしません。
2周目からは以前書き込んだデータがある所を新しいデータで上書きしていくんです。
なので、Queue内部のデータのサイズや先頭位置の管理が非常に重要になります。
さて、本題、メールサーバーは結局この部分が壊れていたんです(笑
えぇ、見つけるのに沢山時間が掛かりましたわ
単純機能のリングバッファですが、その制御は大変複雑でとても大変でした。
2周目以降の話ですが・・・・Queueからデータを取るときは、ダブルバッファ専用のプログラムを用意してあり、これをアプリケーション側から呼び出すのですが、
間違いはこのデータ取り出しプログラムにありました。
データのサイズチェック、同期等、基本的な部分は全部逢っていたのですが、この取り出しプログラムは、「1行」を取り出すちょっと特殊な物だったのです。
サイズチェックが入っているので、1周前の改行で反応するようなことはないのですが、
行末のチェックするところから終了した時に、終了がエラー(行末がまだない)かどうか調べるところで、その条件が甘かったのです。
行末かどうかのチェックは、さすがに全部のデータを見ないといけないので、前から順番に見て行き、行末が見つかった時点で終了します。無ければ順番に見ていく関係バッファの最後で終了します。
バッファの最後にたどり着いたかどうかは、調べているために見ている場所がバッファの最後にたどり着いたかどうかで調べることができます。
バッファの最後は
Queueの先頭+データサイズ
で算出されますが、残念ながら本当の最後のデータは
バッファの最後−1の所になります。それはQueueの先頭にもデータが含まれるため単純に足しただけだとこうなってしまいます。
なのでデータ最後かの確認は
見ている場所<バッファの最後
になります。(≦ではなく<になります)
見ている場所を+1するたびにこの条件で調べるので行末が見つからなかったときこの見ている場所は
見ている場所=バッファの最後
になっているんです。
それなのに、このチェックが終わった後にエラーかどうか調べる方法を
見ている場所に行末があるか調べていたので、行末が見つからなかったときに、運悪く1周前の行末がバッファの最後にあると、行末と誤認する問題があったのです(笑
何故こんな方法を採ったかというと、
行末が見つかったときは、その判別にこの方が手っ取り早い
プログラムは如何に楽に作るかが問題です(笑
結局これではダメなので、バッファの最後に達していないかのチェックを追加することで今回の不具合を解消しましたが、でも冗長ですよね〜
データの最後に達していないときは改行が見つかったとき以外はないはずなので、そこに改行があるかどうかを調べる必要はないと思うのですが、でも・・・取り外したことによって他に不具合が増えるのが怖いので、無駄に2回も行末かどうかのチェックをしていることになります。
でも、こんなのでも良いよね。
そもそも間違えてたのはTEA.mil☆ミちゃんだし(笑
☆ティア☆
2002/4/20