/*---------------------------------------------------------------------------* Project: Horizon File: rdt_Transceiver.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. 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. $Rev: 46347 $ *---------------------------------------------------------------------------*/ #include "stdafx.h" #include "rdt_Transceiver.h" #include #include "Test.h" #include "rdt_Segment.h" #include "rdt_Stopwatch.h" #include "rdt_Utility.h" namespace { #ifdef _WIN32 /* Please see man pages for details */ int SendRawData(SOCKET &sock, const void *pBuf, size_t bufSize) { ASSERT(pBuf!=NULL); ASSERT(bufSize > 0); const int c_flags = 0; int ret = ::send(sock, static_cast(pBuf), bufSize, c_flags); if(ret < 1) { int err = WSAGetLastError(); VERBOSE("::send() returned an error. (%d)\n", err); if(err==WSAEWOULDBLOCK) { // Resource temporarily unavailable VERBOSE("Because the error code was WSAEWOULDBLOCK, it continues. \n"); return 0; } else if(err==WSAECONNRESET) { // Connection reset by peer. VERBOSE("The error code was WSAECONNRESET. It continues. \n"); return 0; } else if(err==WSAECONNABORTED) { // Software caused connection abort. VERBOSE("The error code was WSAECONNABORTED. It continues. \n"); return 0; } else { PANIC("Stop. err = %d\n", err); return -1; } } else { return ret; } } /* Please see man pages for details */ int RecvRawData(SOCKET &sock, void *pBuf, size_t bufSize) { int ret = ::recv(sock, static_cast(pBuf), bufSize, 0); if(ret < 0) { int err = WSAGetLastError(); switch(err) { case WSAEWOULDBLOCK: // The data simply was not there. return 0; case WSAECONNRESET: LOG("The remote connection was lost, so the data could not be acquired. \n"); return -1; case WSAECONNABORTED: LOG("The host aborted the connection. \n"); return -1; default: LOG("The ::recv function returned an unknown error. (%d)\n", err); return -1; } } else { return ret; } } #elif defined(NN_PLATFORM_CTR) // Unlike the Windows version, added an argument to acquire the UDS error. int SendRawData(nn::uds::EndpointDescriptor *pSendEp, u16 nodeId, u8 port, const void *pBuf, size_t bufSize, nn::Result *pResult) { using namespace nn::uds; ASSERT(pSendEp!=NULL); ASSERT(pBuf!=NULL); ASSERT(bufSize > 0); ASSERT(bufSize <= UDS_PACKET_PAYLOAD_MAX_SIZE); // Do not exceed the UDS packet maximum payload size. ASSERT(pResult!=NULL); const bit8 c_option = NO_WAIT; // Measured with the stopwatch. static nn::rdt::CTR::detail::Stopwatch s_sw("uds::SendTo()"); s_sw.Start(); *pResult = SendTo(*pSendEp, pBuf, bufSize, nodeId, port, c_option); s_sw.Stop(); if(pResult->IsSuccess()) { VERBOSE("Sent data.\n"); return bufSize; } else { nn::rdt::CTR::PrintResultCode(*pResult); LOG("SendTo() Failed.\n"); return -1; } } // Unlike the Windows version, added an argument to acquire the UDS error. int RecvRawData(nn::uds::EndpointDescriptor *pRecvEp, void *pBuf, size_t bufSize, nn::Result *pResult) { using namespace nn::uds; ASSERT(pRecvEp!=NULL); ASSERT(pBuf!=NULL); ASSERT(bufSize > 0); ASSERT(bufSize <= UDS_PACKET_PAYLOAD_MAX_SIZE); // Do not exceed the UDS packet maximum payload size. ASSERT(pResult!=NULL); u16 srcNodeId = 0x0000; size_t received = 0; const bit8 opt = nn::uds::NO_WAIT; // Measured with the stopwatch. static nn::rdt::CTR::detail::Stopwatch s_sw("uds::ReceiveFrom()"); s_sw.Start(); *pResult = ReceiveFrom(*pRecvEp, pBuf, &received, &srcNodeId, bufSize, opt); s_sw.Stop(); if(pResult->IsSuccess()) { VERBOSE("Received. Src Node: %u\n", srcNodeId); return received; } else { if(pResult->GetSummary()==nn::Result::SUMMARY_NOT_FOUND) { VERBOSE("It seems that data is not arrived. It is not an error.\n"); return 0; } else { nn::rdt::CTR::PrintResultCode(*pResult); LOG("ReceiveFrom() failed.\n"); return -1; } } } #endif // end of _WIN32 #ifdef _WIN32 u16 port = 0; nn::rdt::CTR::Segment s_sendSeg; nn::rdt::CTR::Segment s_recvSeg; const char *c_msg = "Transceiver Unit Test\n"; // For single unit tests. void serverSide(LPVOID pParam) { using namespace nn::rdt::CTR; SOCKET sock = SetupServerSide(port); // Wait until the data arrives. SleepCurrentThread(1000); Transceiver t; nn::Result result = t.Initialize(sock); CU_ASSERT(result.IsSuccess()); result = t.Pull(&s_recvSeg); CU_ASSERT(result.IsSuccess()); LOG("The content of the received content, \n"); s_recvSeg.PrintDebugInfo(); LOG("cleans up the server-side socket. \n"); CleanupServerSide(sock); } // For single unit tests. static void clientSide(LPVOID pParam) { using namespace nn::rdt::CTR; SOCKET sock = SetupClientSide(port); Transceiver t; nn::Result result = t.Initialize(sock); CU_ASSERT(result.IsSuccess()); s_sendSeg.SetData(c_msg, strlen(c_msg)); t.Put(s_sendSeg); // Segment send // TCP session end. LOG("Cleanup client socket. \n"); CleanupClientSide(sock); } #endif // end of _WIN32 } // End of anonymous namespace namespace nn { namespace rdt { namespace CTR { Transceiver::Transceiver(void) :m_initialized(false) { } Transceiver::~Transceiver(void) { Finalize(); } #ifdef _WIN32 nn::Result Transceiver::Initialize(SOCKET sock) { if(m_initialized) { return ResultAlreadyInitialized(); } else { m_sock = sock; m_initialized = true; return ResultSuccess(); } } #elif defined(NN_PLATFORM_CTR) nn::Result Transceiver::Initialize(u16 nodeId, u8 port) { if(m_initialized) { return ResultAlreadyInitialized(); } else { nn::Result result; result = nn::uds::CreateEndpoint(&m_sendEp); if(result.IsFailure()) { return result; } result = nn::uds::CreateEndpoint(&m_recvEp); if(result.IsFailure()) { nn::Result r = nn::uds::DestroyEndpoint(&m_sendEp); NN_UTIL_PANIC_IF_FAILED(r); return result; } result = nn::uds::Attach(&m_recvEp, nodeId, port); // Feature close to bind() + connect() in the socket. Must be before the Receive function. if(result.IsFailure()) { nn::Result r; r = nn::uds::DestroyEndpoint(&m_recvEp); NN_UTIL_PANIC_IF_FAILED(r); r = nn::uds::DestroyEndpoint(&m_sendEp); NN_UTIL_PANIC_IF_FAILED(r); return result; } m_remoteNodeId = nodeId; m_port = port; m_initialized = true; VERBOSE("Transceiver initialized.\n"); return ResultSuccess(); } } #else #error no platform selected #endif #ifdef _WIN32 void Transceiver::Finalize(void) { if(m_initialized) { m_initialized = false; } else { // Do nothing. } } #elif defined(NN_PLATFORM_CTR) void Transceiver::Finalize(void) { if(m_initialized) { m_initialized = false; nn::Result result; result = nn::uds::DestroyEndpoint(&m_recvEp); NN_UTIL_PANIC_IF_FAILED(result); result = nn::uds::DestroyEndpoint(&m_sendEp); NN_UTIL_PANIC_IF_FAILED(result); } else { // Do nothing. } } #else #error no platform selected #endif // Send segments. nn::Result Transceiver::Put(const Segment &seg) { ASSERT(m_initialized); #ifdef _WIN32 int n = SendRawData(m_sock, &seg, sizeof(Segment)); #else nn::Result ret; int n = SendRawData(&m_sendEp, m_remoteNodeId, m_port, &seg, sizeof(Segment), &ret); #endif if(n==sizeof(Segment)) { // State for which sending probably was done. // Whether it actually arrived to the remote side is a different issue. VERBOSE("The segment send probably went well. \n"); return ResultSuccess(); } else { VERBOSE("Tried to send the segment, but the SendRawData function seemed to fail.... (n = %d)\n", n); #ifdef _WIN32 return MakeDisconnected(); // Because the Windows environment does not have UDS, use the MakeDisconnected function. #else return ret; // The UDS error is returned as-is. #endif } } // Receive segment. nn::Result Transceiver::Pull(Segment *pSeg) { ASSERT(m_initialized); ASSERT(pSeg!=NULL); #ifdef _WIN32 int n = RecvRawData(m_sock, pSeg, sizeof(Segment)); #else nn::Result ret; int n = RecvRawData(&m_recvEp, pSeg, sizeof(Segment), &ret); #endif if((n==sizeof(Segment)) && pSeg->IsValid()) { // LOG("Received Segment:\n"); // pSeg->PrintDebugInfo(); return ResultSuccess(); } else if(n==0) { // It is only that the data hasn't arrived yet. Through. return ResultNoData(); } else { // Question about the connection termination. LOG("It seems that size of segment is wrong(%d byte)\n", n); #ifdef _WIN32 return MakeDisconnected(); // Since the Windows environment does not have UDS, use the MakeDisconnected function. #else return ret; // The UDS error is returned as-is. #endif } } #ifdef _WIN32 void Transceiver::Test(void) { port = GetAvailablePort(); // Create the server and client thread. HANDLE hThread[2]; hThread[0] = (HANDLE)_beginthread(serverSide, 0, NULL); hThread[1] = (HANDLE)_beginthread(clientSide, 0, NULL); // Wait for thread to end. WaitForMultipleObjects(2, hThread, TRUE, INFINITE); char buf[Segment::PAYLOAD_SIZE]; u32 sz = s_recvSeg.GetData(buf, Segment::PAYLOAD_SIZE); buf[sz] = '\0'; CU_ASSERT(strcmp(c_msg, buf)==0); } #endif /* memo WSAECONNRESET 10054 Connection reset by peer. An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET. WSAECONNABORTED 10053 Software caused connection abort. An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error. */ }}} // namespace nn::rdt::CTR