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("Because the error code was WSAEWOULDBLOCK, it continues. \n");
50 return 0;
51 }
52 else if(err==WSAECONNRESET)
53 {
54 // Connection reset by peer.
55 VERBOSE("The error code was WSAECONNRESET. It continues. \n");
56 return 0;
57 }
58 else if(err==WSAECONNABORTED)
59 {
60 // Software caused connection abort.
61 VERBOSE("The error code was WSAECONNABORTED. It 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 // The data simply was not there.
92 return 0;
93 case WSAECONNRESET:
94 LOG("The remote connection was lost, so the data could not be acquired. \n");
95 return -1;
96 case WSAECONNABORTED:
97 LOG("The host aborted the connection. \n");
98 return -1;
99 default:
100 LOG("The ::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 // Unlike the Windows version, added an argument to acquire the UDS error.
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); // Do not exceed the UDS packet maximum payload size.
120 ASSERT(pResult!=NULL);
121
122 const bit8 c_option = NO_WAIT;
123
124 // Measured with the stopwatch.
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 // Unlike the Windows version, added an argument to acquire the UDS error.
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); // Do not exceed the 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 // Measured with the 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 the 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 content, \n");
209 s_recvSeg.PrintDebugInfo();
210
211 LOG("cleans up 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 send
227
228 // TCP session end.
229 LOG("Cleanup client 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 close to bind() + connect() in the socket. Must be before the 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 // Send segments.
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 // State for which sending probably was done.
360 // Whether it actually arrived to the remote side is a different issue.
361 VERBOSE("The segment send probably went well. \n");
362 return ResultSuccess();
363 }
364 else
365 {
366 VERBOSE("Tried to send the segment, but the SendRawData function seemed to fail.... (n = %d)\n", n);
367 #ifdef _WIN32
368 return MakeDisconnected(); // Because the Windows environment does not have UDS, use the MakeDisconnected function.
369 #else
370 return ret; // The UDS error is returned 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 // It is only that the data hasn't arrived yet. Through.
397 return ResultNoData();
398 }
399 else
400 {
401 // Question about the connection termination.
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, use the MakeDisconnected function.
405 #else
406 return ret; // The UDS error is returned 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 the server and client thread.
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