VirtualBox

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

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

Audio/Validation Kit: Implemented support for client-mode connection timeouts. ​bugref:10008

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

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