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