1 /*--------------------------------------------------------------------------
2   Project:  HorizonSDK
3   File:     rdt_Transceiver.cpp
4   Copyright 2009 Nintendo. All rights reserved.
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law. They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10   $Date:: 2010-07-28#$
11   $Rev: 21987 $
12   $Author: hiratsu_daisuke $
13  *-------------------------------------------------------------------------
14 
15 
16 */
17 
18 #include "stdafx.h"
19 
20 #include "rdt_Transceiver.h"
21 
22 #include <nn/rdt/CTR/rdt_Result.h>
23 
24 #include "Test.h"
25 #include "rdt_Segment.h"
26 #include "rdt_Stopwatch.h"
27 #include "rdt_Utility.h"
28 
29 namespace
30 {
31 
32 #ifdef _WIN32
33     /* Please see man pages for details
34 
35 
36      */
SendRawData(SOCKET & sock,const void * pBuf,size_t bufSize)37     int SendRawData(SOCKET &sock, const void *pBuf, size_t bufSize)
38     {
39         ASSERT(pBuf!=NULL);
40         ASSERT(bufSize > 0);
41 
42         const int c_flags = 0;
43         int ret = ::send(sock, static_cast<const char*>(pBuf), bufSize, c_flags);
44         if(ret < 1)
45         {
46             int err = WSAGetLastError();
47             VERBOSE("::send() returned an error. (%d)\n", err);
48             if(err==WSAEWOULDBLOCK)
49             {
50                 // Resource temporarily unavailable
51                 VERBOSE("Continues because the error code was WSAEWOULDBLOCK. \n");
52                 return 0;
53             }
54             else if(err==WSAECONNRESET)
55             {
56                 // Connection reset by peer.
57                 VERBOSE("The error code was WSAECONNRESET. Continues. \n");
58                 return 0;
59             }
60             else if(err==WSAECONNABORTED)
61             {
62                 // Software caused connection abort.
63                 VERBOSE("The error code was WSAECONNABORTED. Continues. \n");
64                 return 0;
65             }
66             else
67             {
68                 PANIC("Stop. err = %d\n", err);
69                 return -1;
70             }
71         }
72         else
73         {
74             return ret;
75         }
76     }
77 
78 
79     /* Please see man pages for details
80 
81 
82 
83      */
RecvRawData(SOCKET & sock,void * pBuf,size_t bufSize)84     int RecvRawData(SOCKET &sock, void *pBuf, size_t bufSize)
85     {
86         int ret = ::recv(sock, static_cast<char*>(pBuf), bufSize, 0);
87         if(ret < 0)
88         {
89             int err = WSAGetLastError();
90             switch(err)
91             {
92             case WSAEWOULDBLOCK:
93                 // Simply, there was no data.
94                 return 0;
95             case WSAECONNRESET:
96                 LOG("Because the remote connection disappeared, data could not be obtained. \n");
97                 return -1;
98             case WSAECONNABORTED:
99                 LOG("The host side aborted the connection. \n");
100                 return -1;
101             default:
102                 LOG("::recv function returned an unknown error. (%d)\n", err);
103                 return -1;
104             }
105         }
106         else
107         {
108             return ret;
109         }
110     }
111 
112 #elif defined(NN_PLATFORM_CTR)
113 
114     // Different from the Windows version, added an argument to get uds errors.
115     int SendRawData(nn::uds::EndpointDescriptor *pSendEp, u16 nodeId, u8 port, const void *pBuf, size_t bufSize, nn::Result *pResult)
116     {
117         using namespace nn::uds;
118         ASSERT(pSendEp!=NULL);
119         ASSERT(pBuf!=NULL);
120         ASSERT(bufSize > 0);
121         ASSERT(bufSize <= UDS_PACKET_PAYLOAD_MAX_SIZE);     // Does not exceed UDS packet maximum payload size
122         ASSERT(pResult!=NULL);
123 
124         const bit8 c_option = NO_WAIT;
125 
126         // Measure with stop watch.
127         static nn::rdt::CTR::detail::Stopwatch s_sw("uds::SendTo()");
128         s_sw.Start();
129         *pResult = SendTo(*pSendEp, pBuf, bufSize, nodeId, port, c_option);
130         s_sw.Stop();
131 
132         if(pResult->IsSuccess())
133         {
134             VERBOSE("Sent data.\n");
135             return bufSize;
136         }
137         else
138         {
139             nn::rdt::CTR::PrintResultCode(*pResult);
140             LOG("SendTo() Failed.\n");
141             return -1;
142         }
143     }
144 
145     // Different from the Windows version, added an argument to get uds errors.
146     int RecvRawData(nn::uds::EndpointDescriptor *pRecvEp, void *pBuf, size_t bufSize, nn::Result *pResult)
147     {
148         using namespace nn::uds;
149         ASSERT(pRecvEp!=NULL);
150         ASSERT(pBuf!=NULL);
151         ASSERT(bufSize > 0);
152         ASSERT(bufSize <= UDS_PACKET_PAYLOAD_MAX_SIZE);     // Does not exceed UDS packet maximum payload size
153         ASSERT(pResult!=NULL);
154 
155         u16 srcNodeId = 0x0000;
156         size_t received = 0;
157         const bit8 opt = nn::uds::NO_WAIT;
158 
159         // Measure with stopwatch.
160         static nn::rdt::CTR::detail::Stopwatch s_sw("uds::ReceiveFrom()");
161         s_sw.Start();
162         *pResult = ReceiveFrom(*pRecvEp, pBuf, &received, &srcNodeId, bufSize, opt);
163         s_sw.Stop();
164 
165         if(pResult->IsSuccess())
166         {
167             VERBOSE("Received.  Src Node: %u\n", srcNodeId);
168             return received;
169         }
170         else
171         {
172             if(pResult->GetSummary()==nn::Result::SUMMARY_NOT_FOUND)
173             {
174                 VERBOSE("It seems that data is not arrived.  It is not an error.\n");
175                 return 0;
176             }
177             else
178             {
179                 nn::rdt::CTR::PrintResultCode(*pResult);
180                 LOG("ReceiveFrom() failed.\n");
181                 return -1;
182             }
183         }
184     }
185 #endif // end of _WIN32
186 
187 
188 #ifdef _WIN32
189     u16 port = 0;
190     nn::rdt::CTR::Segment s_sendSeg;
191     nn::rdt::CTR::Segment s_recvSeg;
192     const char *c_msg = "Transceiver Unit Test\n";
193 
194     // For single unit tests.
serverSide(LPVOID pParam)195     void serverSide(LPVOID pParam)
196     {
197         using namespace nn::rdt::CTR;
198         SOCKET sock = SetupServerSide(port);
199 
200         // Wait until data arrives
201         SleepCurrentThread(1000);
202 
203         Transceiver t;
204         nn::Result result = t.Initialize(sock);
205         CU_ASSERT(result.IsSuccess());
206 
207         result = t.Pull(&s_recvSeg);
208         CU_ASSERT(result.IsSuccess());
209 
210         LOG("The content of the received segment is \n");
211         s_recvSeg.PrintDebugInfo();
212 
213         LOG("Cleanup of the server-side socket. \n");
214         CleanupServerSide(sock);
215     }
216 
217 
218     // For single unit tests.
clientSide(LPVOID pParam)219     static void clientSide(LPVOID pParam)
220     {
221         using namespace nn::rdt::CTR;
222         SOCKET sock = SetupClientSide(port);
223 
224         Transceiver t;
225         nn::Result result = t.Initialize(sock);
226         CU_ASSERT(result.IsSuccess());
227         s_sendSeg.SetData(c_msg, strlen(c_msg));
228         t.Put(s_sendSeg);  // Segment transmission
229 
230         // TCP session end
231         LOG("Cleanup of the client-side socket. \n");
232         CleanupClientSide(sock);
233     }
234 #endif  // end of _WIN32
235 
236 }  // End of anonymous namespace
237 
238 namespace nn { namespace rdt { namespace CTR {
239 
240 
Transceiver(void)241 Transceiver::Transceiver(void)
242      :m_initialized(false)
243 {
244 }
245 
246 
~Transceiver(void)247 Transceiver::~Transceiver(void)
248 {
249     Finalize();
250 }
251 
252 
253 #ifdef _WIN32
Initialize(SOCKET sock)254 nn::Result Transceiver::Initialize(SOCKET sock)
255 {
256     if(m_initialized)
257     {
258         return ResultAlreadyInitialized();
259     }
260     else
261     {
262         m_sock = sock;
263         m_initialized = true;
264         return ResultSuccess();
265     }
266 }
267 #elif defined(NN_PLATFORM_CTR)
Initialize(u16 nodeId,u8 port)268 nn::Result Transceiver::Initialize(u16 nodeId, u8 port)
269 {
270     if(m_initialized)
271     {
272         return ResultAlreadyInitialized();
273     }
274     else
275     {
276         nn::Result result;
277         result = nn::uds::CreateEndpoint(&m_sendEp);
278         if(result.IsFailure())
279         {
280             return result;
281         }
282 
283         result = nn::uds::CreateEndpoint(&m_recvEp);
284         if(result.IsFailure())
285         {
286             nn::Result r = nn::uds::DestroyEndpoint(&m_sendEp);
287             NN_UTIL_PANIC_IF_FAILED(r);
288             return result;
289         }
290 
291         result = nn::uds::Attach(&m_recvEp, nodeId, port);  // Feature similar to bind() + connect() for the socket. Required before Receive function.
292         if(result.IsFailure())
293         {
294             nn::Result r;
295             r = nn::uds::DestroyEndpoint(&m_recvEp);
296             NN_UTIL_PANIC_IF_FAILED(r);
297             r = nn::uds::DestroyEndpoint(&m_sendEp);
298             NN_UTIL_PANIC_IF_FAILED(r);
299             return result;
300         }
301 
302         m_remoteNodeId = nodeId;
303         m_port = port;
304         m_initialized = true;
305         VERBOSE("Transceiver initialized.\n");
306         return ResultSuccess();
307     }
308 }
309 #else
310     #error no platform selected
311 #endif
312 
313 
314 #ifdef _WIN32
Finalize(void)315 void Transceiver::Finalize(void)
316 {
317     if(m_initialized)
318     {
319         m_initialized = false;
320     }
321     else
322     {
323         // Do nothing.
324     }
325 }
326 #elif defined(NN_PLATFORM_CTR)
Finalize(void)327 void Transceiver::Finalize(void)
328 {
329     if(m_initialized)
330     {
331         m_initialized = false;
332         nn::Result result;
333         result = nn::uds::DestroyEndpoint(&m_recvEp);
334         NN_UTIL_PANIC_IF_FAILED(result);
335         result = nn::uds::DestroyEndpoint(&m_sendEp);
336         NN_UTIL_PANIC_IF_FAILED(result);
337     }
338     else
339     {
340         // Do nothing.
341     }
342 }
343 #else
344     #error no platform selected
345 #endif
346 
347 
348 // Sends segment.
Put(const Segment & seg)349 nn::Result Transceiver::Put(const Segment &seg)
350 {
351     ASSERT(m_initialized);
352 
353 #ifdef _WIN32
354     int n = SendRawData(m_sock, &seg, sizeof(Segment));
355 #else
356     nn::Result ret;
357     int n = SendRawData(&m_sendEp, m_remoteNodeId, m_port, &seg, sizeof(Segment), &ret);
358 #endif
359     if(n==sizeof(Segment))
360     {
361         // Condition where the transmission probably was made.
362         // Whether it actually arrived to the remote side is a different issue.
363         VERBOSE("The segment transmission probably was successful. \n");
364         return ResultSuccess();
365     }
366     else
367     {
368         VERBOSE("An attempt to send the segment was made but it seems that it failed with SendRawData()... (n = %d)\n", n);
369 #ifdef _WIN32
370         return MakeDisconnected();  // Since the Windows environment does not have UDS, we reluctantly evade the issue with MakeDisconnected()
371 #else
372         return ret;                 // Return the UDS errors as is
373 #endif
374     }
375 }
376 
377 
378 // Receive segment.
Pull(Segment * pSeg)379 nn::Result Transceiver::Pull(Segment *pSeg)
380 {
381     ASSERT(m_initialized);
382     ASSERT(pSeg!=NULL);
383 
384 #ifdef _WIN32
385     int n = RecvRawData(m_sock, pSeg, sizeof(Segment));
386 #else
387     nn::Result ret;
388     int n = RecvRawData(&m_recvEp, pSeg, sizeof(Segment), &ret);
389 #endif
390     if((n==sizeof(Segment)) && pSeg->IsValid())
391     {
392 //        LOG("received segment:\n");
393 //        pSeg->PrintDebugInfo();
394         return ResultSuccess();
395     }
396     else if(n==0)
397     {
398         // Data has not yet arrived. Through.
399         return ResultNoData();
400     }
401     else
402     {
403         // Suspect connection interruption.
404         LOG("It seems that size of segment is wrong(%d byte)\n", n);
405 #ifdef _WIN32
406         return MakeDisconnected();  // Since the Windows environment does not have UDS, we reluctantly evade the issue with MakeDisconnected()
407 #else
408         return ret;                 // Return the UDS errors as is
409 #endif
410     }
411 }
412 
413 
414 #ifdef _WIN32
Test(void)415 void Transceiver::Test(void)
416 {
417     port = GetAvailablePort();
418 
419     // Create server and client threads
420     HANDLE hThread[2];
421 
422     hThread[0] = (HANDLE)_beginthread(serverSide, 0, NULL);
423     hThread[1] = (HANDLE)_beginthread(clientSide, 0, NULL);
424 
425     // Wait for thread to end
426     WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
427 
428     char buf[Segment::PAYLOAD_SIZE];
429     u32 sz = s_recvSeg.GetData(buf, Segment::PAYLOAD_SIZE);
430     buf[sz] = '\0';
431     CU_ASSERT(strcmp(c_msg, buf)==0);
432 }
433 #endif
434 
435 
436 /*
437 memo
438 WSAECONNRESET
439 10054
440 
441 Connection reset by peer.
442 An existing connection was forcibly closed by the remote host.
443 This normally results if the peer application on the remote host is suddenly stopped,
444 the host is rebooted, the host or remote network interface is disabled,
445 or the remote host uses a hard close
446 (see setsockopt for more information on the SO_LINGER option on the remote socket).
447 This error may also result if a connection was broken due to keep-alive activity
448 detecting a failure while one or more operations are in progress.
449 Operations that were in progress fail with WSAENETRESET.
450 Subsequent operations fail with WSAECONNRESET.
451 WSAECONNABORTED
452 10053
453 
454 Software caused connection abort.
455 An established connection was aborted by the software in your host computer,
456 possibly due to a data transmission time-out or protocol error.
457 
458 
459 
460 
461 
462 
463 
464 
465 */
466 
467 }}} // namespace nn::rdt::CTR
468