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