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