前回作成した、SimpleServerTransFile(Windows、Linux)、
SimpleClientTransFile(Windows、Linux)の送信・受信とバッファの関係は下図のように
なっています。
【クライアントA,クライアントB】
1.キー入力された文字列は、送受信スレッドの送信バッファ(m_pSendData)に
格納されます。
格納されたデータは送受信スレッドによって読み込まれ送信されます。
【サーバ】
2.受信されたデータは受信バッファ(szRecvBuffer)に格納されます。
格納されたデータを解析し、チャットメッセージを配信するためにチャット用バッファ
(m_pRecvData)に格納されます。
3.格納されたデータはメインスレッドで順番に読み込まれん、配信対象の送受信スレッドの
送信バッファ(m_pSendData)に格納されます。
格納されたデータは送受信スレッドによって読み込まれ送信されます。
【クライアントC】
4.受信されたデータは受信バッファ(szRecvBuffer)に格納されます。
格納されたデータを解析し、チャットメッセージを表示します。
これらのバッファには、1組のデータ(パケット)しか格納できないようにしています。
前のデータが未処理の時、次のデータを格納しようとしたときにはエラーとしています。
これでは、ファイル送受信など次々にデータを処理したいときに、うまくいかなかったり
速度が出なかったりしてしまいます。
そこで、ここでは改良ポイントとしてこれらのバッファをリングバッファに置き換える
ことをやってみましょう。
ここで作成するリングバッファは、マルチスレッドからのアクセスを考慮して同期
オブジェクトも使うことにしましょう。
また、以降のセクションで必要に応じて機能追加もすることにします。
先ずはリングバッファってどんなものかを説明します。
リングバッファとは、データをリング状に 記録するバッファのことで、データを 無限に連続して記憶することができます。 この時、データは、もっとも古いものから 上書きされていきます。 通信データの様に、連続して沢山のデータ をやり取りする場合に有効な手法です。 当然のことながら、古いデータは勝手に 上書きされていくので、上書きされる前に データを読み出す必要があります。
Head = Tailの時はバッファに何も入っていないと定義します。
読み出し可能エリアサイズ(DataSize)は次のように求められます。
0—-Head—–Tail—–AllocSizeの順の時
DataSize:T-H
0—-Tail—–Haed—–AllocSizeの順の時
DataSize:(A-H) + T
(*)HとTが一致した時は格納されているデータ数を0とするので
書き込み可能エリアの最後はHの1バイト前までしか許可しないようにします。
これを実現するクラスCRingBuffを作ります
このクラスが公開するメソッドは次の7個です。
long Read(LPBYTE pbBuf, long lSize = 1);
機能:リングバッファから指定されたバイト数分のデータを取得する
引数:LPBYTE pbBuf [in/out]バッファ
long lSize [in]バッファサイズ
返値:実際に読み込んだサイズ
long ReadWithoutUpdateHeadPoint(LPBYTE pbBuf, long lSize = 1);
機能:リングバッファから指定されたバイト数分のデータを取得する
ただし読み込みポインタを更新しない
受信データの解析のために、仮読みをする機能
引数:LPBYTE pbBuf [in/out]バッファ
long lSize [in]バッファサイズ
返値:実際に読み込んだサイズ
void UpdateHeadPoint(long lSize = 1);
機能:読み込みポインタを指定されたバイト数分進める
仮読みしたデータをリングバッファから読み込み済みにする機能
引数:long lSize [in]読んだことにするバイト数
返値:なし
BOOL Write(LPBYTE pbBuf, long lSize = 1);
機能:リングバッファに指定されたバイト数分のデータを書き込む
引数:LPBYTE pbBuf [in]バッファ
long lSize [in]バッファサイズ
返値:TRUE:書き込み成功 FALSE:書き込み失敗(空き不足)
long GetReadableSize();
機能:リングバッファに格納されているデータ数を取得する
引数:なし
返値:格納されているデータ数
long GetWriteableSize();
機能:リングバッファに書き込めるデータ数を取得する
引数:なし
返値:書き込めるデータ数
void Clear();
機能:リングバッファを空にする
書き込み、読み込みポインタの初期化
引数:なし
返値:なし
それでは、実際のコード(クラスCRingBuff)を見てみます。
HとTが一致した時は格納されているデータ数を0と定義しているので書き込み
可能エリアの最後はHの1バイト前までしか許可しないようにするため
バッファの最大サイズのほかに、1バイト分多い確保サイズ(m_lAllocSize)を
定義しています。
それでは、実際のコード(クラスCRingBuff)を見てみます。
HとTが一致した時は格納されているデータ数を0と定義しているので書き込み
可能エリアの最後はHの1バイト前までしか許可しないようにするため
バッファの最大サイズのほかに、1バイト分多い確保サイズ(m_lAllocSize)を
定義しています。
Read、Writeを実施する際に、読み込み可能サイズ、書き込み可能サイズを取得する
必要があるので、同期処理のないGetReadableSize0、GetWriteableSize0を定義
しています。
【RingBuff.h】 #pragma once #include “stdThread.h” #include “MySyncObject.h” // 書きこむ位置が lTail // 読み出す位置が lHead // lHead = lTailの時はバッファに何も入っていないと定義 // 0====Head=====Tail=====AllocSize size:T-H // 0====Tail=====Haed=====AllocSize size:(A-H) + T class CRingBuff { public: CRingBuff(long lMaxSize); ~CRingBuff(void); long Read(LPBYTE pbBuf, long lSize = 1); long ReadWithoutUpdateHeadPoint(LPBYTE pbBuf, long lSize = 1); void UpdateHeadPoint(long lSize = 1); BOOL Write(LPBYTE pbBuf, long lSize = 1); long GetReadableSize(); long GetWriteableSize(); void Clear(); private: long GetReadableSize0(); // GetReadableSizeの同期処理なし版 long GetWriteableSize0(); // GetWriteableSizeの同期なし版 LPBYTE m_pbData; // バッファの実体 long m_lMaxSize; // バッファの最大サイズ long m_lAllocSize; // 実体の確保サイズ=バッファの最大サイズ+1 long m_lHead; // 読み込み開始位置 long m_lTail; // 書き込み開始位置 CMySyncObject *m_pCMySyncObject; // 同期用 public: BOOL m_fInitialized; // 初期化が完了しているか };
【RingBuff.cpp】 #include “RingBuff.h” //======================================================== // Function // クラスの構築、バッファの最大サイズを指定 // Parameter // long lMaxSize [in]格納可能な最大サイズ // Return // なし //======================================================== CRingBuff::CRingBuff(long lMaxSize) { m_fInitialized = FALSE; m_pbData = NULL; m_lMaxSize = lMaxSize; // サイズを覚える m_lAllocSize = m_lMaxSize + 1; // メモリを確保するサイズ m_lHead = 0; // 読み込み開始位置 m_lTail = 0; // 書き込み開始位置 m_pCMySyncObject = new CMySyncObject(); m_pCMySyncObject->Initialize(); if (lMaxSize > 0) m_pbData = (LPBYTE)calloc(m_lAllocSize, sizeof(BYTE)); if (m_pbData != NULL) m_fInitialized = TRUE; // 初期化完了 } //======================================================== // Function // クラスの破棄、バッファの開放 // Parameter // なし // Return // なし //======================================================== CRingBuff::~CRingBuff(void) { SAFE_FREE(m_pbData) m_pCMySyncObject->Uninitialize(); SAFE_DELETE(m_pCMySyncObject) m_fInitialized = FALSE; } //======================================================== // Function // データの読み込み // 現在の読み込み開始位置から読み込む // Parameter // LPBYTE pbBuf [in/out]バッファ // long lSize [in]バッファサイズ // Return // 実際に読み込んだサイズ <= バッファサイズ //======================================================== long CRingBuff::Read(LPBYTE pbBuf, long lSize) { long lReadSize = 0; long ii; // 読み込み可能サイズ(格納されているデータ数)を取得し // それ以上は読み込まないようにする m_pCMySyncObject->Lock(); lReadSize = min(GetReadableSize0(), lSize); for (ii = 0; ii < lReadSize; ++ii) { pbBuf[ii] = m_pbData[m_lHead++]; // 読み込み位置更新 m_lHead = m_lHead % m_lAllocSize; } m_pCMySyncObject->UnLock(); return(lReadSize); } //======================================================== // Function // データの仮読み込み // 現在の読み込み開始位置から読み込む // 読み込み位置の更新をしない // Parameter // LPBYTE pbBuf [in/out]バッファ // long lSize [in]バッファサイズ // Return // 実際に読み込んだサイズ //======================================================== long CRingBuff::ReadWithoutUpdateHeadPoint(LPBYTE pbBuf, long lSize) { long lReadSize = 0; long lHead, ii; // 読み込み可能サイズ(格納されているデータ数)を取得し // それ以上は読み込まないようにする m_pCMySyncObject->Lock(); lReadSize = min(GetReadableSize0(), lSize); lHead = m_lHead; for (ii = 0; ii < lReadSize; ++ii) { pbBuf[ii] = m_pbData[lHead++]; // 読み込み位置は更新していない lHead = lHead % m_lAllocSize; } m_pCMySyncObject->UnLock(); return(lReadSize); } //======================================================== // Function // 読み込み位置の更新をする(指定バイト読んだことにする) // Parameter // long lSize [in]読んだことにするバイト数 // Return // なし //======================================================== void CRingBuff::UpdateHeadPoint(long lSize) { m_pCMySyncObject->Lock(); m_lHead = (m_lHead + lSize) % m_lAllocSize; m_pCMySyncObject->UnLock(); } //======================================================== // Function // データの書き込み // 現在の書き込み開始位置から書き込む // この関数を呼ぶ前にGetWriteableSizeで書き込み可能サイズを調べること // Parameter // LPBYTE pbBuf [in]バッファ // long lSize [in]バッファサイズ // Return // TRUE:書き込み成功 FALSE:空き不足 //======================================================== BOOL CRingBuff::Write(LPBYTE pbBuf, long lSize) { BOOL fRet = FALSE; long ii; // 書き込み可能サイズ(バッファの空き)を取得し書き込めるか調べる m_pCMySyncObject->Lock(); if(lSize > GetWriteableSize0()) goto L_END; for (ii = 0; ii < lSize; ++ii) { m_pbData[m_lTail++] = pbBuf[ii]; m_lTail = m_lTail % m_lAllocSize; } fRet = TRUE; L_END: m_pCMySyncObject->UnLock(); return(fRet); } //======================================================== // Function // 読み込み可能サイズ(格納されているデータ数)の取得 // Parameter // なし // Return // 読み込み可能サイズ //======================================================== long CRingBuff::GetReadableSize() { long lHead, lTail, lReadable = 0; m_pCMySyncObject->Lock(); if (m_pbData == NULL) goto L_END; lHead = m_lHead; lTail = m_lTail; lReadable = (lHead <= lTail) ? (lTail – lHead) : (m_lAllocSize – lHead) + lTail; L_END: m_pCMySyncObject->UnLock(); return(lReadable); } //======================================================== // Function // 読み込み可能サイズ(格納されているデータ数)の取得 // 同期なし版(呼び出し元Read、ReadWithoutUpdateHeadPointで同期処理を行う) // Parameter // なし // Return // 読み込み可能サイズ //======================================================== long CRingBuff::GetReadableSize0() { long lHead, lTail, lReadable = 0; if (m_pbData == NULL) goto L_END; lHead = m_lHead; lTail = m_lTail; lReadable = (lHead <= lTail) ? (lTail – lHead) : (m_lAllocSize – lHead) + lTail; L_END: return(lReadable); } //======================================================== // Function // 書き込み可能サイズ(空きサイズ)の取得 // Parameter // なし // Return // 書き込み可能サイズ //======================================================== long CRingBuff::GetWriteableSize() { long lHead, lTail, lWritable = 0; m_pCMySyncObject->Lock(); if (m_pbData == NULL) goto L_END; lTail = m_lTail; lHead = m_lHead; lWritable = (lTail < lHead) ? (lHead – lTail) : (m_lAllocSize – lTail) + lHead; if (lWritable > 0) // HaedとTailの間に隙間を開けるため1バイト減らす –lWritable; L_END: m_pCMySyncObject->UnLock(); return(lWritable); } //======================================================== // Function // 書き込み可能サイズ(空きサイズ)の取得 // 同期なし版(呼び出し元Writeで同期を行う) // Parameter // なし // Return // 書き込み可能サイズ //======================================================== long CRingBuff::GetWriteableSize0() { long lHead, lTail, lWritable = 0; if (m_pbData == NULL) goto L_END; lTail = m_lTail; lHead = m_lHead; lWritable = (lTail < lHead) ? (lHead – lTail) : (m_lAllocSize – lTail) + lHead; if (lWritable > 0) // HaedとTailの間に隙間を開けるため1バイト減らす –lWritable; L_END: return(lWritable); } //======================================================== // Function // 書き込み、読み込みポインタの初期化 // Parameter // なし // Return // なし //======================================================== void CRingBuff::Clear() { m_pCMySyncObject->Lock(); m_lTail = 0; m_lHead = 0; m_pCMySyncObject->UnLock(); }
各バッファをCRingBuffで置き換えるのですが、それは次回としましょう。