1 /*--------------------------------------------------------------------------
2 Project: HorizonSDK
3 File: rdt_Transceiver.cpp
4
5 Copyright 2009 Nintendo. 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 $Date:: 2010-07-28#$
14 $Rev: 21987 $
15 $Author: hiratsu_daisuke $
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 /*!
34 @brief (信頼性のない通信路を経由して)データを送信します。生データの送信に使う、内部実装です。
35 pBufで指定されるデータには何も付け足したり、間引かれたりせずに送受信されます。
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()がエラーを返しました。(%d)\n", err);
48 if(err==WSAEWOULDBLOCK)
49 {
50 // Resource temporarily unavailable
51 VERBOSE("エラーコードはWSAEWOULDBLOCKだったので、続行します。\n");
52 return 0;
53 }
54 else if(err==WSAECONNRESET)
55 {
56 // Connection reset by peer.
57 VERBOSE("エラーコードはWSAECONNRESETでした。続行します。\n");
58 return 0;
59 }
60 else if(err==WSAECONNABORTED)
61 {
62 // Software caused connection abort.
63 VERBOSE("エラーコードはWSAECONNABORTEDでした。続行します。\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 /*!
80 @brief データを受信します。生データの受信に使う、内部実装です。
81 pBufで指定されるバッファには、何も付け足したり、間引かれたりしていない
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 // 単にデータが無かっただけ。
94 return 0;
95 case WSAECONNRESET:
96 LOG("リモートのコネクションが消失したので、データを取得できません。\n");
97 return -1;
98 case WSAECONNABORTED:
99 LOG("ホスト側でコネクションが中断(アボート)されました。\n");
100 return -1;
101 default:
102 LOG("::recv()が未知のエラーを返しました。(%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 // Windows版と異なり、udsのエラーを取得するための引数を追加している。
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); // UDS パケットの最大ペイロードサイズを越えない
122 ASSERT(pResult!=NULL);
123
124 const bit8 c_option = NO_WAIT;
125
126 // ストップウォッチで計測。
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 // Windows版と異なり、udsのエラーを取得するための引数を追加している。
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); // UDS パケットの最大ペイロードサイズを越えない
153 ASSERT(pResult!=NULL);
154
155 u16 srcNodeId = 0x0000;
156 size_t received = 0;
157 const bit8 opt = nn::uds::NO_WAIT;
158
159 // ストップウォッチで計測。
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 // 単体テスト用。
serverSide(LPVOID pParam)195 void serverSide(LPVOID pParam)
196 {
197 using namespace nn::rdt::CTR;
198 SOCKET sock = SetupServerSide(port);
199
200 // データが届くのを待つ
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("受信したセグメントの内容は、\n");
211 s_recvSeg.PrintDebugInfo();
212
213 LOG("サーバー側のソケットの後始末をします。\n");
214 CleanupServerSide(sock);
215 }
216
217
218 // 単体テスト用。
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); // セグメント送信
229
230 // TCPセッション終了
231 LOG("クライアント側のソケットの後始末をします。\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); // socket における bind() + connect() に近い機能。Receive() の前に必須。
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 // セグメントの送信を行います
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 // たぶん送信できたであろう、という状況。
362 // 実際にリモートに届いたかどうかは別問題。
363 VERBOSE("セグメントの送信は、たぶんうまくいきました。\n");
364 return ResultSuccess();
365 }
366 else
367 {
368 VERBOSE("セグメントの送信を試みましたが、SendRawData()に失敗したようです…。(n = %d)\n", n);
369 #ifdef _WIN32
370 return MakeDisconnected(); // Windows環境ではUDSが無いので、しかたなくMakeDisconnected()でお茶を濁す
371 #else
372 return ret; // UDSのエラーをそのまま返す
373 #endif
374 }
375 }
376
377
378 // セグメントを受信します。
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("受信セグメント:\n");
393 // pSeg->PrintDebugInfo();
394 return ResultSuccess();
395 }
396 else if(n==0)
397 {
398 // データがまだ到着していないだけ。スルー。
399 return ResultNoData();
400 }
401 else
402 {
403 // コネクション断絶の疑い。
404 LOG("It seems that size of segment is wrong(%d byte)\n", n);
405 #ifdef _WIN32
406 return MakeDisconnected(); // Windows環境ではUDSが無いので、しかたなくMakeDisconnected()でお茶を濁す
407 #else
408 return ret; // UDSのエラーをそのまま返す
409 #endif
410 }
411 }
412
413
414 #ifdef _WIN32
Test(void)415 void Transceiver::Test(void)
416 {
417 port = GetAvailablePort();
418
419 // サーバーとクライアントのスレッドを作成
420 HANDLE hThread[2];
421
422 hThread[0] = (HANDLE)_beginthread(serverSide, 0, NULL);
423 hThread[1] = (HANDLE)_beginthread(clientSide, 0, NULL);
424
425 // スレッド終了待ち
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
439 WSAECONNRESET
440 10054
441
442 Connection reset by peer.
443 An existing connection was forcibly closed by the remote host.
444 This normally results if the peer application on the remote host is suddenly stopped,
445 the host is rebooted, the host or remote network interface is disabled,
446 or the remote host uses a hard close
447 (see setsockopt for more information on the SO_LINGER option on the remote socket).
448 This error may also result if a connection was broken due to keep-alive activity
449 detecting a failure while one or more operations are in progress.
450 Operations that were in progress fail with WSAENETRESET.
451 Subsequent operations fail with WSAECONNRESET.
452
453
454
455 WSAECONNABORTED
456 10053
457
458 Software caused connection abort.
459 An established connection was aborted by the software in your host computer,
460 possibly due to a data transmission time-out or protocol error.
461
462
463
464
465 */
466
467 }}} // namespace nn::rdt::CTR
468