/*-------------------------------------------------------------------------- Project: HorizonSDK File: rdt_SenderImpl.h Copyright 2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2010-09-13#$ $Rev: 25720 $ $Author: hiratsu_daisuke $ *-------------------------------------------------------------------------*/ #include "stdafx.h" #ifndef NN_RDT_SENDERIMPL_H_ #define NN_RDT_SENDERIMPL_H_ #include "rdt_HostBase.h" #include "rdt_SendBuffer.h" #include "rdt_ResendQueue.h" #include "rdt_Segment.h" namespace nn { namespace rdt { namespace CTR { // 先行宣言 class SenderStateBase; /*! @brief これはSenderクラスの内部実装のためのクラスです。 */ class SenderImpl : public HostBase{ public: /*! @brief 初期化します。 */ SenderImpl(void) throw(); /*! @brief デストラクタです。仮にFinalize()が呼ばれていなくても、この中で自動的に呼び出されます。 */ virtual ~SenderImpl(void); /*! @brief 初期化します。 */ #ifdef _WIN32 nn::Result Initialize(SOCKET sock, void *pSendBuf, u16 sendBufSize); #elif defined(NN_PLATFORM_CTR) nn::Result Initialize(u16 nodeId, u8 port, void *pSendBuf, u16 sendBufSize); #endif /*! @brief 解放します。 */ void Finalize(void); /*! @brief 接続要求を出すリクエストを発行します。 リクエストのキューがいっぱいであるなどの理由で 失敗することがあります。 */ nn::Result Open(void); /*! @brief 接続を閉じるリクエストを発行します。 リクエストのキューがいっぱいであるなどの理由で 失敗することがあります。 */ nn::Result Close(void); /*! @brief データをRDTライブラリの送信バッファに書き込み、送信の準備をします。 送信バッファがいっぱいの場合は、失敗します。このとき、送信バッファには データは1バイトたりとも書き込まれません。 この関数呼び出しで渡したデータが実際に送信されるのは、もう少し後になることに 注意して下さい。 */ nn::Result Send(const void *pBuf, size_t bufSize); /*! @brief 通信処理を進行させます。実際の通信処理は、この関数の内部で実行されます。 アプリ側から、毎フレーム程度の間隔で呼び出してもらうことを想定しています。 */ nn::Result Process(void); /*! @brief 処理の中断をリクエストします。 */ void Cancel(void); /*! @brief オブジェクトの状態を取得します。 */ enum SenderState GetStatus(void) const; /*! @brief 配置new演算子です。 */ static void* operator new(size_t size, void *pBuf) throw() { if(size!=sizeof(SenderImpl)) { PANIC("Wrong size.\n"); return NULL; } if(pBuf==NULL) { PANIC("NULL pointer is detected.\n"); return NULL; } ALIGN_ASSERT(pBuf, 8); return pBuf; } // 配置deleteを記述したのだが、delete(m_pImpl, m_workBuf)のように // 呼ぶと、何故かグローバルなdelete(void *p)が呼び出されているようだった。 // Webにも上記の説を補強する記述が見つかったので、deleteは定義しないで // 置こうとした。…しかし、それだとRealViewコンパイラにWarningで叱られた。 // よって定義だけはしておく。ただし呼ばないこと。 // SenderImplは、それを使う人が明示的にデストラクタを呼ぶこと。 // C++ Labyringth (http://www.fides.dti.ne.jp/~oka-t/cpplab-placement-new-2.html) // もし operator delete( void *p ) と両方が定義されていた場合は、 // operator delete( void *p ) のほうが呼び出される。 これは C++ の // 規格書に述べられていることなのだが、 なんとも不明瞭な仕様だと // 言わざるをえない。昔の仕様との整合性を重んじた結果だろうか。 static void operator delete (void *p) throw() { PANIC("Do not call this delete! Please call destructor manually.\n"); (void)p; } /*! @brief Senderインスタンスの詳細な内部状態をプリントします。(デバッグ用) */ void PrintDebugInfo(void) const; /*! @brief SenderImplクラスの内部構造を知りたくなったときに作った関数です(デバッグ用) */ static void PrintProperty(void); /*! @brief CUnitの単体テストです。 */ static void Test(void); private: /*! @brief コピーコンストラクタは封印します。 */ SenderImpl (const SenderImpl&); /*! @brief 代入演算子は封印します。 */ SenderImpl& operator=(const SenderImpl&); // プライベート関数群。 void changeState(void); // この関数を呼ぶことで、状態遷移が実行される。 void setNextState(SenderStateBase *p); // 次の状態をセット。 void putSegmentWithResend(const Segment &seg); // segには、受信できたセグメントを与える。 // 受信セグメントに対する共通処理がこの関数の中に // まとめられている。 void processReceivedSegment(const Segment &seg); // 一連の再送処理を実行する。 // セグメントの再送が行われた場合はtrueが返る。 // 行われなかった場合はfalse。 bool processResending(void); // キャンセル処理を実行。 void processCanceling(void); // 送信バッファにデータが溜まっていたら、1つのセグメントに // 収まる量だけ吸い出して、送信します。 void sendData(void); // SYN要求を込めたセグメントを作成し、送信します。 void sendSynSegment(void); // FIN要求を込めたセグメントを作成し、送信します。 // この関数は、送信バッファがカラッポの状況で呼び出す必要があります。 void sendFinSegment(void); // 送信バッファはカラッポ? bool isSendBufferEmpty(void) const { return m_sendBuffer.IsEmpty(); } // Closed状態に入るときなどに呼ばれることを想定。 // Stateパターンにおける状態を保持するメンバを除き、 // 各種メンバ変数をまっさらな状態に戻す。 void clear(void); // ISS(初期シーケンス番号)を取得。 u32 getInitialSequenceNumber(void) const { return m_iss; } // 到達未確認のオクテットのうち、最も古いシーケンス番号を取得 u32 getUnacknowledgeNumber(void) const { return m_una; } // 次に送信することになるオクテットのシーケンス番号 u32 getNextSequenceNumber(void) const { return m_nxt; } // リモートから受信したACKは正当? bool isValidAckNumber(u32 ack) const; // メンバ変数群。 SendBuffer m_sendBuffer; // 送信バッファ u16 m_remoteWindowSize; // リモート側から通知されたウィンドウサイズの最新情報。 bool m_initialized; u8 m_padding; // パディング u32 m_iss; // 初期シーケンス番号 u32 m_una; // 到達未確認のシーケンス番号のうち、最も古いもの u32 m_nxt; // 次に送信されるべきオクテットのシーケンス番号 ResendQueue m_resendQueue; // 再送キュー(8バイトアライメントが要求される) // Stateパターンの実践。 SenderStateBase *m_pState; SenderStateBase *m_pNextState; friend class SenderStateOpenRequested; friend class SenderStateOpening; friend class SenderStateOpened; friend class SenderStateCloseRequested; friend class SenderStateClosing; friend class SenderStateClosed; // デバッグ用変数 u32 m_arrivals; // セグメント到着回数 // SenderImplクラスもResendQueueのアライメントに引きずられるので、 // ここでパディングする必要があることもある u8 m_padding2[4]; }; }}} // namespace nn::rdt::CTR #endif // end of NN_RDT_SENDERIMPL_H_