/*-------------------------------------------------------------------------- Project: HorizonSDK File: rdt_ReceiverImpl.cpp 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-14#$ $Rev: 25753 $ $Author: hiratsu_daisuke $ *------------------------------------------------------------------------- */ #include "stdafx.h" #include "rdt_ReceiverImpl.h" #include #include namespace { } // End of anonymous namespace namespace nn { namespace rdt { namespace CTR { // Base class for "State" class ReceiverStateBase{ public: virtual ~ReceiverStateBase(void){} virtual void initialize(ReceiverImpl *pReceiver); // Called when this state is started virtual void finalize (ReceiverImpl *pReceiver); // Called when this state is finished virtual void update (ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const = 0; protected: ReceiverStateBase(void){} }; void ReceiverStateBase::initialize(ReceiverImpl *pReceiver) { (void)pReceiver; } void ReceiverStateBase::finalize(ReceiverImpl *pReceiver) { (void)pReceiver; } void ReceiverStateBase::update(ReceiverImpl *pReceiver) { (void)pReceiver; } class ReceiverStateWaiting : public ReceiverStateBase{ public: static ReceiverStateBase* getInstance(void); virtual void update(ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const; protected: ReceiverStateWaiting(void){} }; class ReceiverStateOpened : public ReceiverStateBase{ public: static ReceiverStateBase* getInstance(void); virtual void update(ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const; protected: ReceiverStateOpened(void){} }; class ReceiverStateWaitingFinished : public ReceiverStateBase{ public: static ReceiverStateBase* getInstance(void); virtual void update(ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const; protected: ReceiverStateWaitingFinished(void){} }; class ReceiverStateFinished : public ReceiverStateBase{ public: static ReceiverStateBase* getInstance(void); virtual void update(ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const; protected: ReceiverStateFinished(void){} }; class ReceiverStateClosed : public ReceiverStateBase{ public: static ReceiverStateBase* getInstance(void); virtual void initialize(ReceiverImpl *pReceiver); virtual void update (ReceiverImpl *pReceiver); virtual enum ReceiverState getStatus(void) const; protected: ReceiverStateClosed(void){} }; // The following is an implementation of an inherited class. ReceiverStateBase* ReceiverStateWaiting::getInstance(void) { static ReceiverStateWaiting s_instance; return &s_instance; } enum ReceiverState ReceiverStateWaiting::getStatus(void) const { return RECEIVER_STATE_WAITING; } void ReceiverStateWaiting::update(ReceiverImpl *pReceiver) { ASSERT(pReceiver!=NULL); //Process here if segment is received. Segment seg; if(pReceiver->pullSegment(&seg).IsSuccess()) { if(seg.IsRst()) { // Per the TCP RFC, it is described that RST received during a LISTEN state should be ignored. //Therefore, nothing is done here. } else if(seg.IsSyn()) { // If this is a proper connection request from a remote source, ACK is returned. // Set receive buffer pReceiver->m_recvBuf.SetInitialSequenceNumber(seg.GetSeqNumber()); // Response to SYN. pReceiver->putSynAckSegment(seg.GetSeqNumber() + 1); pReceiver->setNextState(ReceiverStateOpened::getInstance()); } } } ReceiverStateBase* ReceiverStateOpened::getInstance(void) { static ReceiverStateOpened s_instance; return &s_instance; } enum ReceiverState ReceiverStateOpened::getStatus(void) const { return RECEIVER_STATE_OPENED; } void ReceiverStateOpened::update(ReceiverImpl *pReceiver) { ASSERT(pReceiver!=NULL); //Process here if segment is received. Segment seg; if(pReceiver->pullSegment(&seg).IsSuccess()) { // First, RST bit check if(seg.IsRst()) { pReceiver->setNextState(ReceiverStateClosed::getInstance()); pReceiver->errorHandling(ResultResetReceived()); return; } // Has the segment with the expected sequence number arrived? ReceiveBuffer &rBuf = pReceiver->m_recvBuf; if(seg.GetSeqNumber()==rBuf.GetLatestSequenceNumber()+1) { if(seg.IsData()) { bool result = rBuf.Push(seg.payload, seg.GetDataLength()); if(result) { // Data has been stored in the receive buffer without problems. // In the existing implementation, the ACK is returned, and as a result it is nothing but a ACK packet of the reception side. // So, returning the ACK here was eliminated. } else { LOG("Received data segment, but could not return ACK because receive buffer did not have enough space.\n"); } } else if(seg.IsFin()) { // ACK transmission for the FIN segment pReceiver->putFinAckSegment(seg.GetSeqNumber() + 1); pReceiver->setNextState(ReceiverStateWaitingFinished::getInstance()); } else { seg.PrintDebugInfo(); PANIC("It seems that SEQ number is valid, but unexpected segment. It is not DATA, nor FIN."); } } else if(seg.GetSeqNumber()==rBuf.GetLatestSequenceNumber()) { // If no response for Syn arrives, it may be considered that the Sender resends a Syn request. // Handling for this case. if(seg.IsSyn()) { // Response to SYN. pReceiver->putSynAckSegment(seg.GetSeqNumber() + 1); } } else { VERBOSE("Unexpected SEQ number. (%d)\n", seg.GetSeqNumber()); VERBOSE("SEQ number %d is expected.\n", rBuf.GetLatestSequenceNumber()+1); VERBOSE("Received segment will be ignored.\n"); #if 0 // Because it was not an expected segment, the possibility of a packet loss is suspected. // For now, return ACK quickly for the portion received to this point. pReceiver->putAckSegment(); // If the above code is entered, it actually made it slower. Probably because there are a lot of ACK packets issued ... #endif } } } ReceiverStateBase* ReceiverStateWaitingFinished::getInstance(void) { static ReceiverStateWaitingFinished s_instance; return &s_instance; } enum ReceiverState ReceiverStateWaitingFinished::getStatus(void) const { return RECEIVER_STATE_WAITING_FINISHED; } void ReceiverStateWaitingFinished::update(ReceiverImpl *pReceiver) { // These return ACK for FIN, but it is not assured that the ACK has arrived to the other party even if the Finished state is entered. // // To determine if the ACK arrived at the other party, you must confirm that no resend packet has arrived from the other party after some time has passed. // ASSERT(pReceiver!=NULL); //Process here if segment is received. Segment seg; if(pReceiver->pullSegment(&seg).IsSuccess()) { // RST bit check if(seg.IsRst()) { pReceiver->setNextState(ReceiverStateClosed::getInstance()); pReceiver->errorHandling(ResultResetReceived()); return; } // Has FIN segment arrived? ReceiveBuffer &rBuf = pReceiver->m_recvBuf; if(seg.GetSeqNumber()==rBuf.GetLatestSequenceNumber()+1) { if(seg.IsFin()) { pReceiver->putFinAckSegment(seg.GetSeqNumber() + 1); } else { seg.PrintDebugInfo(); PANIC("Sequence number is valid, but FIN is not included.\n"); } } else { // seg.PrintDebugInfo(); LOG("Segment with unexpected sequence number. Ignored.\n"); } } // State transition after timeout determination. if(pReceiver->isSenderClosed()) { pReceiver->setNextState(ReceiverStateFinished::getInstance()); } } ReceiverStateBase* ReceiverStateFinished::getInstance(void) { static ReceiverStateFinished s_instance; return &s_instance; } enum ReceiverState ReceiverStateFinished::getStatus(void) const { return RECEIVER_STATE_FINISHED; } void ReceiverStateFinished::update(ReceiverImpl *pReceiver) { ASSERT(pReceiver!=NULL); //Process here if segment is received. Segment seg; if(pReceiver->pullSegment(&seg).IsSuccess()) { // TODO: Should send RST for segments that do not include RST. PANIC("State is FINISHED, but received segment from remote!\n"); } } ReceiverStateBase* ReceiverStateClosed::getInstance(void) { static ReceiverStateClosed s_instance; return &s_instance; } enum ReceiverState ReceiverStateClosed::getStatus(void) const { return RECEIVER_STATE_CLOSED; } void ReceiverStateClosed::initialize(ReceiverImpl *pReceiver) { ASSERT(pReceiver!=NULL); pReceiver->clear(); } void ReceiverStateClosed::update(ReceiverImpl *pReceiver) { ASSERT(pReceiver!=NULL); // For segments (not including RST) received while in CLOSED state, respond with RST Segment seg; if(pReceiver->pullSegment(&seg).IsSuccess() && !seg.IsRst()) { LOG("Receiver is in CLOSED state, but received segment. RST will be sent.\n"); if(seg.IsAck()) { pReceiver->sendRstSegment(seg.GetAckNumber()); } else { const u32 SEQ = 0; const u32 ACK = seg.GetSeqNumber() + seg.GetSegmentLength(); pReceiver->sendRstAckSegment(SEQ, ACK); } } } // ReceiverImpl::ReceiverImpl(void) throw() :m_initialized(false) { } // ReceiverImpl::~ReceiverImpl(void) { Finalize(); } #ifdef _WIN32 nn::Result ReceiverImpl::Initialize(SOCKET sock, void *pRecvBuf, u16 recvBufSize) #else nn::Result ReceiverImpl::Initialize(u16 nodeId, u8 port, void *pRecvBuf, u16 recvBufSize) #endif { if(m_initialized) { return ResultAlreadyInitialized(); } else { if(pRecvBuf==NULL) { return ResultNullPointer(); } if(recvBufSize==0) { return ResultInvalidSize(); } #ifdef _WIN32 nn::Result result = HostBase::Initialize(sock); #elif defined(NN_PLATFORM_CTR) nn::Result result = HostBase::Initialize(nodeId, port); #endif if(result.IsFailure()) { return result; } else { m_pState = ReceiverStateClosed::getInstance(); m_pNextState = NULL; m_recvBuf.Initialize(pRecvBuf, recvBufSize); clear(); m_initialized = true; return ResultSuccess(); } } } void ReceiverImpl::Finalize(void) { if(m_initialized) { m_initialized = false; m_recvBuf.Finalize(); HostBase::Finalize(); } else { // Do nothing. } } nn::Result ReceiverImpl::Wait(void) { ASSERT(m_initialized); if(GetStatus()==RECEIVER_STATE_CLOSED) { setNextState(ReceiverStateWaiting::getInstance()); changeState(); return ResultSuccess(); } else { return ResultUntimelyFunctionCall(); } } nn::Result ReceiverImpl::Close(void) { ASSERT(m_initialized); if(GetStatus()==RECEIVER_STATE_FINISHED) { setNextState(ReceiverStateClosed::getInstance()); changeState(); return ResultSuccess(); } else { return ResultUntimelyFunctionCall(); } } nn::Result ReceiverImpl::Receive(void *pBuf, size_t *recvSize, size_t bufSize) { ASSERT(m_initialized); if((pBuf==NULL) || (recvSize==NULL)) { return ResultNullPointer(); } if(bufSize==0) { return ResultDoNothing(); } enum ReceiverState stat = GetStatus(); if((stat!=RECEIVER_STATE_OPENED) && (stat!=RECEIVER_STATE_WAITING_FINISHED) && (stat!=RECEIVER_STATE_FINISHED)) { return ResultUntimelyFunctionCall(); } *recvSize = m_recvBuf.Read(pBuf, bufSize); if(*recvSize > 0) { m_recvBuf.Pop(*recvSize); } // Using Receive as the trigger, notify window remotely. // Since space in the receive window was made, send segment for the purpose of remote notification. // If attempting to send a segment while not in the OPENED state, a state check is performed with the if-statement because there is a possibility that the connection has been interrupted. // if(GetStatus()==RECEIVER_STATE_OPENED) { putAckSegment(); } return ResultSuccess(); } nn::Result ReceiverImpl::Process(void) { ASSERT(m_initialized); // update function for each state. if(m_pState) { m_pState->update(this); } // State Transitions changeState(); // Error returned here. // If returned, reset error. nn::Result ret = GetErrorCode(); errorHandling(ResultSuccess()); return ret; } enum ReceiverState ReceiverImpl::GetStatus(void) const { ASSERT(m_initialized); ASSERTMSG(m_pState!=NULL, "It seems that state is not initialized.\n"); return m_pState->getStatus(); } void ReceiverImpl::Cancel(void) { ASSERT(m_initialized); // The cancel process executes immediately without waiting for the Process function. // RST transmission const u32 seq = 0; sendRstSegment(seq); // To Closed state immediately. setNextState(ReceiverStateClosed::getInstance()); changeState(); } // State Transitions. Do nothing specific if the next state is not set. void ReceiverImpl::changeState(void) { if(m_pNextState) { if(m_pState) { m_pState->finalize(this); } m_pState = m_pNextState; m_pNextState = NULL; m_pState->initialize(this); } } void ReceiverImpl::setNextState(ReceiverStateBase *p) { ASSERT(p!=NULL); ASSERT(m_pNextState==NULL); m_pNextState = p; } void ReceiverImpl::putSynAckSegment(u32 ack) { Segment a; a.ClearHeader(); a.SetAckNumber(ack); a.SetWindowSize(m_recvBuf.GetRestSize()); putSegment(a); } void ReceiverImpl::putAckSegment(void) { Segment a; a.ClearHeader(); a.SetAckNumber(m_recvBuf.GetLatestSequenceNumber()+1); a.SetWindowSize(m_recvBuf.GetRestSize()); putSegment(a); } void ReceiverImpl::putFinAckSegment(u32 ack) { Segment a; a.ClearHeader(); a.SetAckNumber(ack); a.SetWindowSize(m_recvBuf.GetRestSize()); putSegment(a); m_finAckSentTime = GetCurrentTimeAsMillisecond(); } bool ReceiverImpl::isSenderClosed(void) const { ASSERT(m_finAckSentTime!=0); // LOG("m_finAckSentTime = %lld\n", m_finAckSentTime); // LOG("Current: %lld\n", GetCurrentTimeAsMillisecond()); return GetCurrentTimeAsMillisecond() - m_finAckSentTime > FIN_TIMEOUT; } void ReceiverImpl::clear(void) { m_recvBuf.Clear(); m_finAckSentTime = 0; } void ReceiverImpl::PrintDebugInfo(void) const { LOG("-- Receiver debug information --\n"); LOG("Current state: %d\n", GetStatus()); m_recvBuf.PrintDebugInfo(); LOG("\n"); } }}} // namespace nn::rdt::CTR