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