VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestServiceTcp.cpp@ 91024

最後變更 在這個檔案從91024是 91024,由 vboxsync 提交於 4 年 前

Audio/Validation Kit: Trying to resolve the connection issues by checking whether clients were connected in reverse mode or not. This would otherwise end up in connecting more and more clients without any real use. See comments. ​bugref:10008

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 30.2 KB
 
1/* $Id: AudioTestServiceTcp.cpp 91024 2021-08-31 09:56:26Z vboxsync $ */
2/** @file
3 * AudioTestServiceTcp - Audio test execution server, TCP/IP Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_AUDIO_TEST
23#include <iprt/log.h>
24
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/critsect.h>
28#include <iprt/err.h>
29#include <iprt/mem.h>
30#include <iprt/message.h>
31#include <iprt/poll.h>
32#include <iprt/string.h>
33#include <iprt/tcp.h>
34#include <iprt/thread.h>
35#include <iprt/time.h>
36
37#include <VBox/log.h>
38
39#include "AudioTestService.h"
40#include "AudioTestServiceInternal.h"
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * TCP specific client data.
53 */
54typedef struct ATSTRANSPORTCLIENT
55{
56 /** Socket of the current client. */
57 RTSOCKET hTcpClient;
58 /** Indicates whether \a hTcpClient comes from the server or from a client
59 * connect (relevant when closing it). */
60 bool fFromServer;
61 /** The size of the stashed data. */
62 size_t cbTcpStashed;
63 /** The size of the stashed data allocation. */
64 size_t cbTcpStashedAlloced;
65 /** The stashed data. */
66 uint8_t *pbTcpStashed;
67} ATSTRANSPORTCLIENT;
68
69/**
70 * Structure for keeping Audio Test Service (ATS) transport instance-specific data.
71 */
72typedef struct ATSTRANSPORTINST
73{
74 /** Critical section for serializing access. */
75 RTCRITSECT CritSect;
76 /** Connection mode to use. */
77 ATSCONNMODE enmConnMode;
78 /** The addresses to bind to. Empty string means any. */
79 char szBindAddr[256];
80 /** The TCP port to listen to. */
81 uint32_t uBindPort;
82 /** The addresses to connect to if running in reversed (VM NATed) mode. */
83 char szConnectAddr[256];
84 /** The TCP port to connect to if running in reversed (VM NATed) mode. */
85 uint32_t uConnectPort;
86 /** Pointer to the TCP server instance. */
87 PRTTCPSERVER pTcpServer;
88 /** Thread calling RTTcpServerListen2. */
89 RTTHREAD hThreadServer;
90 /** Thread calling RTTcpClientConnect. */
91 RTTHREAD hThreadConnect;
92 /** The main thread handle (for signalling). */
93 RTTHREAD hThreadMain;
94 /** Stop connecting attempts when set. */
95 bool fStopConnecting;
96 /** Connect cancel cookie. */
97 PRTTCPCLIENTCONNECTCANCEL volatile pConnectCancelCookie;
98} ATSTRANSPORTINST;
99/** Pointer to an Audio Test Service (ATS) TCP/IP transport instance. */
100typedef ATSTRANSPORTINST *PATSTRANSPORTINST;
101
102/**
103 * Structure holding an ATS connection context, which is
104 * required when connecting a client via server (listening) or client (connecting).
105 */
106typedef struct ATSCONNCTX
107{
108 /** Pointer to transport instance to use. */
109 PATSTRANSPORTINST pInst;
110 /** Pointer to transport client to connect. */
111 PATSTRANSPORTCLIENT pClient;
112 /** Connection timeout (in ms).
113 * Use RT_INDEFINITE_WAIT to wait indefinitely. */
114 uint32_t msTimeout;
115} ATSCONNCTX;
116/** Pointer to an Audio Test Service (ATS) TCP/IP connection context. */
117typedef ATSCONNCTX *PATSCONNCTX;
118
119
120/*********************************************************************************************************************************
121* Global Variables *
122*********************************************************************************************************************************/
123
124/**
125 * Disconnects the current client and frees all stashed data.
126 */
127static void atsTcpDisconnectClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
128{
129 RT_NOREF(pThis);
130
131 if (pClient->hTcpClient != NIL_RTSOCKET)
132 {
133 LogRelFlowFunc(("%RTsock\n", pClient->hTcpClient));
134
135 int rc;
136 if (pClient->fFromServer)
137 rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
138 else
139 rc = RTTcpClientClose(pClient->hTcpClient);
140 pClient->hTcpClient = NIL_RTSOCKET;
141 AssertRCSuccess(rc);
142 }
143
144 if (pClient->pbTcpStashed)
145 {
146 RTMemFree(pClient->pbTcpStashed);
147 pClient->pbTcpStashed = NULL;
148 }
149}
150
151/**
152 * Sets the current client socket in a safe manner.
153 *
154 * @returns NIL_RTSOCKET if consumed, otherwise hTcpClient.
155 * @param pThis Transport instance.
156 * @param pClient Client to set the socket for.
157 * @param fFromServer Whether the socket is from a server (listening) or client (connecting) call.
158 * Important when closing / disconnecting.
159 * @param hTcpClient The client socket.
160 */
161static RTSOCKET atsTcpSetClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, bool fFromServer, RTSOCKET hTcpClient)
162{
163 RTCritSectEnter(&pThis->CritSect);
164 if ( pClient->hTcpClient == NIL_RTSOCKET
165 && !pThis->fStopConnecting)
166 {
167 LogRelFlowFunc(("New client %RTsock connected (fFromServer=%RTbool)\n", hTcpClient, fFromServer));
168
169 pClient->fFromServer = fFromServer;
170 pClient->hTcpClient = hTcpClient;
171 hTcpClient = NIL_RTSOCKET; /* Invalidate, as pClient has now ownership. */
172 }
173 RTCritSectLeave(&pThis->CritSect);
174 return hTcpClient;
175}
176
177/**
178 * Checks if it's a fatal RTTcpClientConnect return code.
179 *
180 * @returns true / false.
181 * @param rc The IPRT status code.
182 */
183static bool atsTcpIsFatalClientConnectStatus(int rc)
184{
185 return rc != VERR_NET_UNREACHABLE
186 && rc != VERR_NET_HOST_DOWN
187 && rc != VERR_NET_HOST_UNREACHABLE
188 && rc != VERR_NET_CONNECTION_REFUSED
189 && rc != VERR_TIMEOUT
190 && rc != VERR_NET_CONNECTION_TIMED_OUT;
191}
192
193/**
194 * Server mode connection thread.
195 *
196 * @returns iprt status code.
197 * @param hSelf Thread handle. Ignored.
198 * @param pvUser Pointer to ATSTRANSPORTINST the thread is bound to.
199 */
200static DECLCALLBACK(int) atsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
201{
202 RT_NOREF(hSelf);
203
204 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
205 PATSTRANSPORTINST pThis = pConnCtx->pInst;
206 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
207
208 /** @todo Implement cancellation support for using pConnCtx->msTimeout. */
209
210 LogRelFlowFuncEnter();
211
212 RTSOCKET hTcpClient;
213 int rc = RTTcpServerListen2(pThis->pTcpServer, &hTcpClient);
214 if (RT_SUCCESS(rc))
215 {
216 hTcpClient = atsTcpSetClient(pThis, pClient, true /* fFromServer */, hTcpClient);
217 RTTcpServerDisconnectClient2(hTcpClient);
218 }
219
220 LogRelFlowFuncLeaveRC(rc);
221 return rc;
222}
223
224/**
225 * Client mode connection thread.
226 *
227 * @returns iprt status code.
228 * @param hSelf Thread handle. Use to sleep on. The main thread will
229 * signal it to speed up thread shutdown.
230 * @param pvUser Pointer to a connection context (PATSCONNCTX) the thread is bound to.
231 */
232static DECLCALLBACK(int) atsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser)
233{
234 PATSCONNCTX pConnCtx = (PATSCONNCTX)pvUser;
235 PATSTRANSPORTINST pThis = pConnCtx->pInst;
236 PATSTRANSPORTCLIENT pClient = pConnCtx->pClient;
237
238 uint64_t msStartTs = RTTimeMilliTS();
239
240 LogRelFlowFuncEnter();
241
242 for (;;)
243 {
244 /* Stop? */
245 RTCritSectEnter(&pThis->CritSect);
246 bool fStop = pThis->fStopConnecting;
247 RTCritSectLeave(&pThis->CritSect);
248 if (fStop)
249 return VINF_SUCCESS;
250
251 /* Try connect. */ /** @todo make cancelable! */
252 RTSOCKET hTcpClient;
253 int rc = RTTcpClientConnectEx(pThis->szConnectAddr, pThis->uConnectPort, &hTcpClient,
254 RT_SOCKETCONNECT_DEFAULT_WAIT, &pThis->pConnectCancelCookie);
255 if (RT_SUCCESS(rc))
256 {
257 hTcpClient = atsTcpSetClient(pThis, pClient, false /* fFromServer */, hTcpClient);
258 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
259 break;
260 }
261
262 if (atsTcpIsFatalClientConnectStatus(rc))
263 return rc;
264
265 if ( pConnCtx->msTimeout != RT_INDEFINITE_WAIT
266 && RTTimeMilliTS() - msStartTs >= pConnCtx->msTimeout)
267 {
268 LogRelFlowFunc(("Timed out (%RU32ms)\n", pConnCtx->msTimeout));
269 return VERR_TIMEOUT;
270 }
271
272 /* Delay a wee bit before retrying. */
273 RTThreadUserWait(hSelf, 1536);
274 }
275
276 LogRelFlowFuncLeave();
277 return VINF_SUCCESS;
278}
279
280/**
281 * Wait on the threads to complete.
282 *
283 * @returns Thread status (if collected), otherwise VINF_SUCCESS.
284 * @param pThis Transport instance.
285 * @param cMillies The period to wait on each thread.
286 */
287static int atsTcpConnectWaitOnThreads(PATSTRANSPORTINST pThis, RTMSINTERVAL cMillies)
288{
289 int rcRet = VINF_SUCCESS;
290
291 LogRelFlowFuncEnter();
292
293 if (pThis->hThreadConnect != NIL_RTTHREAD)
294 {
295 int rcThread;
296 int rc2 = RTThreadWait(pThis->hThreadConnect, cMillies, &rcThread);
297 if (RT_SUCCESS(rc2))
298 {
299 pThis->hThreadConnect = NIL_RTTHREAD;
300 rcRet = rcThread;
301 }
302 }
303
304 if (pThis->hThreadServer != NIL_RTTHREAD)
305 {
306 int rcThread;
307 int rc2 = RTThreadWait(pThis->hThreadServer, cMillies, &rcThread);
308 if (RT_SUCCESS(rc2))
309 {
310 pThis->hThreadServer = NIL_RTTHREAD;
311 if (RT_SUCCESS(rc2))
312 rcRet = rcThread;
313 }
314 }
315
316 LogRelFlowFuncLeaveRC(rcRet);
317 return rcRet;
318}
319
320/**
321 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
322 */
323static DECLCALLBACK(int) atsTcpWaitForConnect(PATSTRANSPORTINST pThis, RTMSINTERVAL msTimeout,
324 bool *pfFromServer, PPATSTRANSPORTCLIENT ppClientNew)
325{
326 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
327 AssertPtrReturn(pClient, VERR_NO_MEMORY);
328
329 int rc;
330
331 LogRelFlowFunc(("msTimeout=%RU32, enmConnMode=%#x\n", msTimeout, pThis->enmConnMode));
332
333 uint64_t msStartTs = RTTimeMilliTS();
334
335 if (pThis->enmConnMode == ATSCONNMODE_SERVER)
336 {
337 /** @todo Implement cancellation support for using \a msTimeout. */
338
339 pClient->fFromServer = true;
340 rc = RTTcpServerListen2(pThis->pTcpServer, &pClient->hTcpClient);
341 LogRelFlowFunc(("RTTcpServerListen2(%RTsock) -> %Rrc\n", pClient->hTcpClient, rc));
342 }
343 else if (pThis->enmConnMode == ATSCONNMODE_CLIENT)
344 {
345 pClient->fFromServer = false;
346 for (;;)
347 {
348 LogRelFlowFunc(("Calling RTTcpClientConnect(%s, %u,)...\n", pThis->szConnectAddr, pThis->uConnectPort));
349 rc = RTTcpClientConnect(pThis->szConnectAddr, pThis->uConnectPort, &pClient->hTcpClient);
350 LogRelFlowFunc(("RTTcpClientConnect(%RTsock) -> %Rrc\n", pClient->hTcpClient, rc));
351 if (RT_SUCCESS(rc) || atsTcpIsFatalClientConnectStatus(rc))
352 break;
353
354 if ( msTimeout != RT_INDEFINITE_WAIT
355 && RTTimeMilliTS() - msStartTs >= msTimeout)
356 {
357 rc = VERR_TIMEOUT;
358 break;
359 }
360
361 if (pThis->fStopConnecting)
362 {
363 rc = VINF_SUCCESS;
364 break;
365 }
366
367 /* Delay a wee bit before retrying. */
368 RTThreadSleep(1536);
369 }
370 }
371 else
372 {
373 Assert(pThis->enmConnMode == ATSCONNMODE_BOTH);
374
375 /*
376 * Create client threads.
377 */
378 RTCritSectEnter(&pThis->CritSect);
379
380 pThis->fStopConnecting = false;
381 RTCritSectLeave(&pThis->CritSect);
382
383 atsTcpConnectWaitOnThreads(pThis, 32 /* cMillies */);
384
385 ATSCONNCTX ConnCtx;
386 RT_ZERO(ConnCtx);
387 ConnCtx.pInst = pThis;
388 ConnCtx.pClient = pClient;
389 ConnCtx.msTimeout = msTimeout;
390
391 rc = VINF_SUCCESS;
392 if (pThis->hThreadConnect == NIL_RTTHREAD)
393 {
394 pThis->pConnectCancelCookie = NULL;
395 rc = RTThreadCreate(&pThis->hThreadConnect, atsTcpClientConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
396 RTTHREADFLAGS_WAITABLE, "tcpconn");
397 }
398 if (pThis->hThreadServer == NIL_RTTHREAD && RT_SUCCESS(rc))
399 rc = RTThreadCreate(&pThis->hThreadServer, atsTcpServerConnectThread, &ConnCtx, 0, RTTHREADTYPE_DEFAULT,
400 RTTHREADFLAGS_WAITABLE, "tcpserv");
401
402 RTCritSectEnter(&pThis->CritSect);
403
404 /*
405 * Wait for connection to be established.
406 */
407 while ( RT_SUCCESS(rc)
408 && pClient->hTcpClient == NIL_RTSOCKET)
409 {
410 RTCritSectLeave(&pThis->CritSect);
411 rc = atsTcpConnectWaitOnThreads(pThis, 10 /* cMillies */);
412 RTCritSectEnter(&pThis->CritSect);
413 }
414
415 /*
416 * Cancel the threads.
417 */
418 pThis->fStopConnecting = true;
419
420 RTCritSectLeave(&pThis->CritSect);
421 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
422 }
423
424 if (RT_SUCCESS(rc))
425 {
426 if (pfFromServer)
427 *pfFromServer = pClient->fFromServer;
428 *ppClientNew = pClient;
429 }
430 else
431 {
432 if (pClient)
433 {
434 RTTcpServerDisconnectClient2(pClient->hTcpClient);
435
436 RTMemFree(pClient);
437 pClient = NULL;
438 }
439 }
440
441 if (RT_FAILURE(rc))
442 LogRelFunc(("Failed with %Rrc\n", rc));
443
444 return rc;
445}
446
447/**
448 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
449 */
450static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
451{
452 LogRelFlowFuncEnter();
453 if (pThis->pTcpServer)
454 {
455 int rc = RTTcpServerDestroy(pThis->pTcpServer);
456 if (RT_FAILURE(rc))
457 LogRelFunc(("RTTcpServerDestroy failed, rc=%Rrc", rc));
458 pThis->pTcpServer = NULL;
459 }
460 LogRelFlowFuncLeave();
461}
462
463/**
464 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
465 */
466static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
467{
468 LogRelFunc(("%RTsock\n", pClient->hTcpClient));
469 atsTcpDisconnectClient(pThis, pClient);
470}
471
472/**
473 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
474 */
475static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
476{
477 LogRelFunc(("%RTsock\n", pClient->hTcpClient));
478
479 /* nothing to do here */
480 RT_NOREF(pThis);
481}
482
483/**
484 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
485 */
486static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
487{
488 /*
489 * Try send the babble reply.
490 */
491 RT_NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
492 int rc;
493 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
494 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
495 while (rc == VERR_INTERRUPTED);
496
497 /*
498 * Disconnect the client.
499 */
500 LogRelFlowFunc(("atsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
501 atsTcpDisconnectClient(pThis, pClient);
502}
503
504/**
505 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
506 */
507static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
508{
509 AssertReturn(pPktHdr->cb >= sizeof(ATSPKTHDR), VERR_INVALID_PARAMETER);
510
511 /*
512 * Write it.
513 */
514 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
515
516 Log3Func(("%RU32 -> %zu\n", pPktHdr->cb, cbToSend));
517
518 LogRelFlowFunc(("%RTsock\n", pClient->hTcpClient));
519 LogRelFlowFunc(("Header:\n"
520 "%.*Rhxd\n", RT_MIN(sizeof(ATSPKTHDR), cbToSend), pPktHdr));
521
522 if (cbToSend > sizeof(ATSPKTHDR))
523 LogRelFlowFunc(("Payload:\n"
524 "%.*Rhxd\n",
525 RT_MIN(64, cbToSend - sizeof(ATSPKTHDR)), (uint8_t *)pPktHdr + sizeof(ATSPKTHDR)));
526
527 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
528 if ( RT_FAILURE(rc)
529 && rc != VERR_INTERRUPTED)
530 {
531 /* assume fatal connection error. */
532 LogRelFunc(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
533 atsTcpDisconnectClient(pThis, pClient);
534 }
535
536 return rc;
537}
538
539/**
540 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
541 */
542static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
543{
544 int rc = VINF_SUCCESS;
545 *ppPktHdr = NULL;
546
547 LogRelFlowFunc(("%RTsock\n", pClient->hTcpClient));
548
549 /*
550 * Read state.
551 */
552 size_t offData = 0;
553 size_t cbData = 0;
554 size_t cbDataAlloced;
555 uint8_t *pbData = NULL;
556
557 /*
558 * Any stashed data?
559 */
560 if (pClient->cbTcpStashedAlloced)
561 {
562 offData = pClient->cbTcpStashed;
563 cbDataAlloced = pClient->cbTcpStashedAlloced;
564 pbData = pClient->pbTcpStashed;
565
566 pClient->cbTcpStashed = 0;
567 pClient->cbTcpStashedAlloced = 0;
568 pClient->pbTcpStashed = NULL;
569 }
570 else
571 {
572 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
573 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
574 if (!pbData)
575 return VERR_NO_MEMORY;
576 }
577
578 /*
579 * Read and validate the length.
580 */
581 while (offData < sizeof(uint32_t))
582 {
583 size_t cbRead;
584 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
585 if (RT_FAILURE(rc))
586 break;
587 if (cbRead == 0)
588 {
589 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
590 rc = VERR_NET_NOT_CONNECTED;
591 break;
592 }
593 offData += cbRead;
594 }
595 if (RT_SUCCESS(rc))
596 {
597 ASMCompilerBarrier(); /* paranoia^3 */
598 cbData = *(uint32_t volatile *)pbData;
599 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
600 {
601 /*
602 * Align the length and reallocate the return packet it necessary.
603 */
604 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
605 if (cbData > cbDataAlloced)
606 {
607 void *pvNew = RTMemRealloc(pbData, cbData);
608 if (pvNew)
609 {
610 pbData = (uint8_t *)pvNew;
611 cbDataAlloced = cbData;
612 }
613 else
614 rc = VERR_NO_MEMORY;
615 }
616 if (RT_SUCCESS(rc))
617 {
618 /*
619 * Read the remainder of the data.
620 */
621 while (offData < cbData)
622 {
623 size_t cbRead;
624 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
625 if (RT_FAILURE(rc))
626 break;
627 if (cbRead == 0)
628 {
629 LogRelFunc(("RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
630 rc = VERR_NET_NOT_CONNECTED;
631 break;
632 }
633
634 offData += cbRead;
635 }
636
637 LogRelFlowFunc(("Header:\n"
638 "%.*Rhxd\n", sizeof(ATSPKTHDR), pbData));
639
640 if ( RT_SUCCESS(rc)
641 && cbData > sizeof(ATSPKTHDR))
642 LogRelFlowFunc(("Payload:\n"
643 "%.*Rhxd\n", RT_MIN(64, cbData - sizeof(ATSPKTHDR)), (uint8_t *)pbData + sizeof(ATSPKTHDR)));
644 }
645 }
646 else
647 {
648 LogRelFunc(("Received invalid packet size (%zu)\n", cbData));
649 rc = VERR_NET_PROTOCOL_ERROR;
650 }
651 }
652 if (RT_SUCCESS(rc))
653 *ppPktHdr = (PATSPKTHDR)pbData;
654 else
655 {
656 /*
657 * Deal with errors.
658 */
659 if (rc == VERR_INTERRUPTED)
660 {
661 /* stash it away for the next call. */
662 pClient->cbTcpStashed = cbData;
663 pClient->cbTcpStashedAlloced = cbDataAlloced;
664 pClient->pbTcpStashed = pbData;
665 }
666 else
667 {
668 RTMemFree(pbData);
669
670 /* assume fatal connection error. */
671 LogRelFunc(("RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
672 atsTcpDisconnectClient(pThis, pClient);
673 }
674 }
675
676 return rc;
677}
678
679/**
680 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
681 */
682static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
683{
684 RT_NOREF(pThis);
685 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
686}
687
688/**
689 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
690 */
691static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
692{
693 RT_NOREF(pThis, pClient);
694 return RTPollSetRemove(hPollSet, idStart);
695}
696
697/**
698 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
699 */
700static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
701{
702 RT_NOREF(pThis);
703 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
704 return RT_SUCCESS(rc);
705}
706
707/**
708 * @interface_method_impl{ATSTRANSPORT,pfnTerm}
709 */
710static DECLCALLBACK(void) atsTcpTerm(PATSTRANSPORTINST pThis)
711{
712 LogRelFlowFuncEnter();
713
714 /* Signal thread */
715 if (RTCritSectIsInitialized(&pThis->CritSect))
716 {
717 RTCritSectEnter(&pThis->CritSect);
718 pThis->fStopConnecting = true;
719 RTCritSectLeave(&pThis->CritSect);
720 }
721
722 if (pThis->hThreadConnect != NIL_RTTHREAD)
723 {
724 RTThreadUserSignal(pThis->hThreadConnect);
725 RTTcpClientCancelConnect(&pThis->pConnectCancelCookie);
726 }
727
728 /* Shut down the server (will wake up thread). */
729 if (pThis->pTcpServer)
730 {
731 LogRelFlowFunc(("Destroying server...\n"));
732 int rc = RTTcpServerDestroy(pThis->pTcpServer);
733 if (RT_FAILURE(rc))
734 LogRelFunc(("RTTcpServerDestroy failed with %Rrc", rc));
735 pThis->pTcpServer = NULL;
736 }
737
738 /* Wait for the thread (they should've had some time to quit by now). */
739 atsTcpConnectWaitOnThreads(pThis, 15000);
740
741 LogRelFlowFuncLeave();
742}
743
744/**
745 * @interface_method_impl{ATSTRANSPORT,pfnCreate}
746 */
747static DECLCALLBACK(int) atsTcpCreate(PATSTRANSPORTINST *ppThis)
748{
749 PATSTRANSPORTINST pThis = (PATSTRANSPORTINST)RTMemAllocZ(sizeof(ATSTRANSPORTINST));
750 AssertPtrReturn(pThis, VERR_NO_MEMORY);
751
752 int rc = RTCritSectInit(&pThis->CritSect);
753 if (RT_SUCCESS(rc))
754 {
755 *ppThis = pThis;
756 }
757
758 return rc;
759}
760
761/**
762 * @interface_method_impl{ATSTRANSPORT,pfnDestroy}
763 */
764static DECLCALLBACK(int) atsTcpDestroy(PATSTRANSPORTINST pThis)
765{
766 /* Finally, clean up the critical section. */
767 if (RTCritSectIsInitialized(&pThis->CritSect))
768 RTCritSectDelete(&pThis->CritSect);
769
770 RTMemFree(pThis);
771
772 return VINF_SUCCESS;
773}
774
775/**
776 * @interface_method_impl{ATSTRANSPORT,pfnStart}
777 */
778static DECLCALLBACK(int) atsTcpStart(PATSTRANSPORTINST pThis)
779{
780 int rc = VINF_SUCCESS;
781
782 if (pThis->enmConnMode != ATSCONNMODE_CLIENT)
783 {
784 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
785 if (RT_FAILURE(rc))
786 {
787 if (rc == VERR_NET_DOWN)
788 {
789 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
790 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
791 uint64_t StartMs = RTTimeMilliTS();
792 do
793 {
794 RTThreadSleep(1000);
795 rc = RTTcpServerCreateEx(pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, &pThis->pTcpServer);
796 } while ( rc == VERR_NET_DOWN
797 && RTTimeMilliTS() - StartMs < 20000);
798 if (RT_SUCCESS(rc))
799 LogRelFunc(("RTTcpServerCreateEx succceeded\n"));
800 }
801
802 if (RT_FAILURE(rc))
803 {
804 LogRelFunc(("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
805 pThis->szBindAddr[0] ? pThis->szBindAddr : NULL, pThis->uBindPort, rc));
806 }
807 }
808 }
809
810 return rc;
811}
812
813/**
814 * @interface_method_impl{ATSTRANSPORT,pfnOption}
815 */
816static DECLCALLBACK(int) atsTcpOption(PATSTRANSPORTINST pThis, int ch, PCRTGETOPTUNION pVal)
817{
818 int rc;
819
820 switch (ch)
821 {
822 case ATSTCPOPT_CONN_MODE:
823 pThis->enmConnMode = (ATSCONNMODE)pVal->u32;
824 return VINF_SUCCESS;
825
826 case ATSTCPOPT_BIND_ADDRESS:
827 rc = RTStrCopy(pThis->szBindAddr, sizeof(pThis->szBindAddr), pVal->psz);
828 if (RT_FAILURE(rc))
829 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
830 if (!pThis->szBindAddr[0])
831 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP bind address specified: %s", pThis->szBindAddr);
832 return VINF_SUCCESS;
833
834 case ATSTCPOPT_BIND_PORT:
835 pThis->uBindPort = pVal->u16;
836 return VINF_SUCCESS;
837
838 case ATSTCPOPT_CONNECT_ADDRESS:
839 rc = RTStrCopy(pThis->szConnectAddr, sizeof(pThis->szConnectAddr), pVal->psz);
840 if (RT_FAILURE(rc))
841 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
842 if (!pThis->szConnectAddr[0])
843 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "No TCP connect address specified");
844 return VINF_SUCCESS;
845
846 case ATSTCPOPT_CONNECT_PORT:
847 pThis->uConnectPort = pVal->u16;
848 return VINF_SUCCESS;
849
850 default:
851 break;
852 }
853 return VERR_TRY_AGAIN;
854}
855
856/**
857 * @interface_method_impl{ATSTRANSPORT,pfnUsage}
858 */
859DECLCALLBACK(void) atsTcpUsage(PRTSTREAM pStream)
860{
861 RTStrmPrintf(pStream,
862 " --tcp-conn-mode <0=both|1=client|2=server>\n"
863 " Selects the connection mode.\n"
864 " Default: 0 (both)\n"
865 " --tcp-bind-addr[ess] <address>\n"
866 " The address(es) to listen to TCP connection on. Empty string\n"
867 " means any address, this is the default.\n"
868 " --tcp-bind-port <port>\n"
869 " The port to listen to TCP connections on.\n"
870 " Default: %u\n"
871 " --tcp-connect-addr[ess] <address>\n"
872 " The address of the server to try connect to in client mode.\n"
873 " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR "\n"
874 " --tcp-connect-port <port>\n"
875 " The port on the server to connect to in client mode.\n"
876 " Default: %u\n"
877 , ATS_TCP_DEF_BIND_PORT_GUEST, ATS_TCP_DEF_CONNECT_PORT_GUEST);
878}
879
880/** Command line options for the TCP/IP transport layer. */
881static const RTGETOPTDEF g_TcpOpts[] =
882{
883 { "--tcp-conn-mode", ATSTCPOPT_CONN_MODE, RTGETOPT_REQ_STRING },
884 { "--tcp-bind-addr", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
885 { "--tcp-bind-address", ATSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
886 { "--tcp-bind-port", ATSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
887 { "--tcp-connect-addr", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
888 { "--tcp-connect-address", ATSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
889 { "--tcp-connect-port", ATSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 }
890};
891
892/** TCP/IP transport layer. */
893const ATSTRANSPORT g_TcpTransport =
894{
895 /* .szName = */ "tcp",
896 /* .pszDesc = */ "TCP/IP",
897 /* .cOpts = */ &g_TcpOpts[0],
898 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
899 /* .pfnUsage = */ atsTcpUsage,
900 /* .pfnCreate = */ atsTcpCreate,
901 /* .pfnDestroy = */ atsTcpDestroy,
902 /* .pfnOption = */ atsTcpOption,
903 /* .pfnStart = */ atsTcpStart,
904 /* .pfnTerm = */ atsTcpTerm,
905 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
906 /* .pfnPollIn = */ atsTcpPollIn,
907 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
908 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
909 /* .pfnRecvPkt = */ atsTcpRecvPkt,
910 /* .pfnSendPkt = */ atsTcpSendPkt,
911 /* .pfnBabble = */ atsTcpBabble,
912 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
913 /* .pfnNotifyBye = */ atsTcpNotifyBye,
914 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
915 /* .u32EndMarker = */ UINT32_C(0x12345678)
916};
917
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette