VirtualBox

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

Last change on this file since 89583 was 89541, checked in by vboxsync, 4 years ago

Audio/ValKit: More code for completely self-contained (self) testing. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: AudioTestServiceTcp.cpp 89541 2021-06-07 09:26:07Z 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.virtualbox.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 RTLOGGROUP_DEFAULT
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#include <iprt/critsect.h>
26#include <iprt/err.h>
27#include <iprt/log.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/poll.h>
31#include <iprt/string.h>
32#include <iprt/tcp.h>
33#include <iprt/thread.h>
34#include <iprt/time.h>
35
36#include "AudioTestService.h"
37#include "AudioTestServiceInternal.h"
38
39
40/*********************************************************************************************************************************
41* Defined Constants And Macros *
42*********************************************************************************************************************************/
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48
49/**
50 * TCP specific client data.
51 */
52typedef struct ATSTRANSPORTCLIENT
53{
54 /** Socket of the current client. */
55 RTSOCKET hTcpClient;
56 /** The size of the stashed data. */
57 size_t cbTcpStashed;
58 /** The size of the stashed data allocation. */
59 size_t cbTcpStashedAlloced;
60 /** The stashed data. */
61 uint8_t *pbTcpStashed;
62} ATSTRANSPORTCLIENT;
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68
69/**
70 * Disconnects the current client and frees all stashed data.
71 */
72static void atsTcpDisconnectClient(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
73{
74 RT_NOREF(pThis);
75
76 if (pClient->hTcpClient != NIL_RTSOCKET)
77 {
78 int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
79 pClient->hTcpClient = NIL_RTSOCKET;
80 AssertRCSuccess(rc);
81 }
82
83 if (pClient->pbTcpStashed)
84 {
85 RTMemFree(pClient->pbTcpStashed);
86 pClient->pbTcpStashed = NULL;
87 }
88}
89
90/**
91 * @interface_method_impl{ATSTRANSPORT,pfnWaitForConnect}
92 */
93static DECLCALLBACK(int) atsTcpWaitForConnect(PATSTRANSPORTINST pThis, PPATSTRANSPORTCLIENT ppClientNew)
94{
95 int rc;
96 RTSOCKET hClientNew;
97
98 rc = RTTcpServerListen2(pThis->pTcpServer, &hClientNew);
99 Log(("atsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc));
100
101 if (RT_SUCCESS(rc))
102 {
103 PATSTRANSPORTCLIENT pClient = (PATSTRANSPORTCLIENT)RTMemAllocZ(sizeof(ATSTRANSPORTCLIENT));
104 if (RT_LIKELY(pClient))
105 {
106 pClient->hTcpClient = hClientNew;
107 pClient->cbTcpStashed = 0;
108 pClient->cbTcpStashedAlloced = 0;
109 pClient->pbTcpStashed = NULL;
110 *ppClientNew = pClient;
111 }
112 else
113 {
114 RTTcpServerDisconnectClient2(hClientNew);
115 rc = VERR_NO_MEMORY;
116 }
117 }
118
119 return rc;
120}
121
122/**
123 * @interface_method_impl{ATSTRANSPORT,pfnNotifyReboot}
124 */
125static DECLCALLBACK(void) atsTcpNotifyReboot(PATSTRANSPORTINST pThis)
126{
127 Log(("atsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", pThis->pTcpServer));
128 if (pThis->pTcpServer)
129 {
130 int rc = RTTcpServerDestroy(pThis->pTcpServer);
131 if (RT_FAILURE(rc))
132 RTMsgInfo("RTTcpServerDestroy failed in atsTcpNotifyReboot: %Rrc", rc);
133 pThis->pTcpServer = NULL;
134 }
135}
136
137/**
138 * @interface_method_impl{ATSTRANSPORT,pfnNotifyBye}
139 */
140static DECLCALLBACK(void) atsTcpNotifyBye(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
141{
142 Log(("atsTcpNotifyBye: atsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
143 atsTcpDisconnectClient(pThis, pClient);
144 RTMemFree(pClient);
145}
146
147/**
148 * @interface_method_impl{ATSTRANSPORT,pfnNotifyHowdy}
149 */
150static DECLCALLBACK(void) atsTcpNotifyHowdy(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
151{
152 /* nothing to do here */
153 RT_NOREF(pThis, pClient);
154}
155
156/**
157 * @interface_method_impl{ATSTRANSPORT,pfnBabble}
158 */
159static DECLCALLBACK(void) atsTcpBabble(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
160{
161 /*
162 * Try send the babble reply.
163 */
164 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
165 int rc;
166 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
167 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
168 while (rc == VERR_INTERRUPTED);
169
170 /*
171 * Disconnect the client.
172 */
173 Log(("atsTcpBabble: atsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
174 atsTcpDisconnectClient(pThis, pClient);
175}
176
177/**
178 * @interface_method_impl{ATSTRANSPORT,pfnSendPkt}
179 */
180static DECLCALLBACK(int) atsTcpSendPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PCATSPKTHDR pPktHdr)
181{
182 Assert(pPktHdr->cb >= sizeof(ATSPKTHDR));
183
184 /*
185 * Write it.
186 */
187 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, ATSPKT_ALIGNMENT);
188 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
189 if ( RT_FAILURE(rc)
190 && rc != VERR_INTERRUPTED)
191 {
192 /* assume fatal connection error. */
193 Log(("RTTcpWrite -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
194 atsTcpDisconnectClient(pThis, pClient);
195 }
196
197 return rc;
198}
199
200/**
201 * @interface_method_impl{ATSTRANSPORT,pfnRecvPkt}
202 */
203static DECLCALLBACK(int) atsTcpRecvPkt(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient, PPATSPKTHDR ppPktHdr)
204{
205 int rc = VINF_SUCCESS;
206 *ppPktHdr = NULL;
207
208 /*
209 * Read state.
210 */
211 size_t offData = 0;
212 size_t cbData = 0;
213 size_t cbDataAlloced;
214 uint8_t *pbData = NULL;
215
216 /*
217 * Any stashed data?
218 */
219 if (pClient->cbTcpStashedAlloced)
220 {
221 offData = pClient->cbTcpStashed;
222 cbDataAlloced = pClient->cbTcpStashedAlloced;
223 pbData = pClient->pbTcpStashed;
224
225 pClient->cbTcpStashed = 0;
226 pClient->cbTcpStashedAlloced = 0;
227 pClient->pbTcpStashed = NULL;
228 }
229 else
230 {
231 cbDataAlloced = RT_ALIGN_Z(64, ATSPKT_ALIGNMENT);
232 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
233 if (!pbData)
234 return VERR_NO_MEMORY;
235 }
236
237 /*
238 * Read and valid the length.
239 */
240 while (offData < sizeof(uint32_t))
241 {
242 size_t cbRead;
243 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
244 if (RT_FAILURE(rc))
245 break;
246 if (cbRead == 0)
247 {
248 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
249 rc = VERR_NET_NOT_CONNECTED;
250 break;
251 }
252 offData += cbRead;
253 }
254 if (RT_SUCCESS(rc))
255 {
256 ASMCompilerBarrier(); /* paranoia^3 */
257 cbData = *(uint32_t volatile *)pbData;
258 if (cbData >= sizeof(ATSPKTHDR) && cbData <= ATSPKT_MAX_SIZE)
259 {
260 /*
261 * Align the length and reallocate the return packet it necessary.
262 */
263 cbData = RT_ALIGN_Z(cbData, ATSPKT_ALIGNMENT);
264 if (cbData > cbDataAlloced)
265 {
266 void *pvNew = RTMemRealloc(pbData, cbData);
267 if (pvNew)
268 {
269 pbData = (uint8_t *)pvNew;
270 cbDataAlloced = cbData;
271 }
272 else
273 rc = VERR_NO_MEMORY;
274 }
275 if (RT_SUCCESS(rc))
276 {
277 /*
278 * Read the remainder of the data.
279 */
280 while (offData < cbData)
281 {
282 size_t cbRead;
283 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
284 if (RT_FAILURE(rc))
285 break;
286 if (cbRead == 0)
287 {
288 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
289 rc = VERR_NET_NOT_CONNECTED;
290 break;
291 }
292 offData += cbRead;
293 }
294 }
295 }
296 else
297 rc = VERR_NET_PROTOCOL_ERROR;
298 }
299 if (RT_SUCCESS(rc))
300 *ppPktHdr = (PATSPKTHDR)pbData;
301 else
302 {
303 /*
304 * Deal with errors.
305 */
306 if (rc == VERR_INTERRUPTED)
307 {
308 /* stash it away for the next call. */
309 pClient->cbTcpStashed = cbData;
310 pClient->cbTcpStashedAlloced = cbDataAlloced;
311 pClient->pbTcpStashed = pbData;
312 }
313 else
314 {
315 RTMemFree(pbData);
316
317 /* assume fatal connection error. */
318 Log(("atsTcpRecvPkt: RTTcpRead -> %Rrc -> atsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
319 atsTcpDisconnectClient(pThis, pClient);
320 }
321 }
322
323 return rc;
324}
325
326/**
327 * @interface_method_impl{ATSTRANSPORT,pfnPollSetAdd}
328 */
329static DECLCALLBACK(int) atsTcpPollSetAdd(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
330{
331 RT_NOREF(pThis);
332 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
333}
334
335/**
336 * @interface_method_impl{ATSTRANSPORT,pfnPollSetRemove}
337 */
338static DECLCALLBACK(int) atsTcpPollSetRemove(PATSTRANSPORTINST pThis, RTPOLLSET hPollSet, PATSTRANSPORTCLIENT pClient, uint32_t idStart)
339{
340 RT_NOREF(pThis, pClient);
341 return RTPollSetRemove(hPollSet, idStart);
342}
343
344/**
345 * @interface_method_impl{ATSTRANSPORT,pfnPollIn}
346 */
347static DECLCALLBACK(bool) atsTcpPollIn(PATSTRANSPORTINST pThis, PATSTRANSPORTCLIENT pClient)
348{
349 RT_NOREF(pThis);
350 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
351 return RT_SUCCESS(rc);
352}
353
354/**
355 * @interface_method_impl{ATSTRANSPORT,pfnTerm}
356 */
357static DECLCALLBACK(void) atsTcpTerm(PATSTRANSPORTINST pThis)
358{
359 /* Shut down the server (will wake up thread). */
360 if (pThis->pTcpServer)
361 {
362 Log(("atsTcpTerm: Destroying server...\n"));
363 int rc = RTTcpServerDestroy(pThis->pTcpServer);
364 if (RT_FAILURE(rc))
365 RTMsgInfo("RTTcpServerDestroy failed in atsTcpTerm: %Rrc", rc);
366 pThis->pTcpServer = NULL;
367 }
368
369 Log(("atsTcpTerm: done\n"));
370}
371
372/**
373 * @interface_method_impl{ATSTRANSPORT,pfnInit}
374 */
375static DECLCALLBACK(int) atsTcpInit(PATSTRANSPORTINST pThis, const char *pszBindAddr, uint32_t uBindPort)
376{
377 int rc = RTTcpServerCreateEx(pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, &pThis->pTcpServer);
378 if (RT_FAILURE(rc))
379 {
380 if (rc == VERR_NET_DOWN)
381 {
382 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
383 pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, rc);
384 uint64_t StartMs = RTTimeMilliTS();
385 do
386 {
387 RTThreadSleep(1000);
388 rc = RTTcpServerCreateEx(pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, &pThis->pTcpServer);
389 } while ( rc == VERR_NET_DOWN
390 && RTTimeMilliTS() - StartMs < 20000);
391 if (RT_SUCCESS(rc))
392 RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
393 }
394 if (RT_FAILURE(rc))
395 {
396 pThis->pTcpServer = NULL;
397 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
398 pszBindAddr[0] ? pszBindAddr : NULL, uBindPort, rc);
399 }
400 }
401
402 return rc;
403}
404
405/** TCP/IP transport layer. */
406const ATSTRANSPORT g_TcpTransport =
407{
408 /* .szName = */ "tcp",
409 /* .pszDesc = */ "TCP/IP",
410 /* .pfnInit = */ atsTcpInit,
411 /* .pfnTerm = */ atsTcpTerm,
412 /* .pfnWaitForConnect = */ atsTcpWaitForConnect,
413 /* .pfnPollIn = */ atsTcpPollIn,
414 /* .pfnPollSetAdd = */ atsTcpPollSetAdd,
415 /* .pfnPollSetRemove = */ atsTcpPollSetRemove,
416 /* .pfnRecvPkt = */ atsTcpRecvPkt,
417 /* .pfnSendPkt = */ atsTcpSendPkt,
418 /* .pfnBabble = */ atsTcpBabble,
419 /* .pfnNotifyHowdy = */ atsTcpNotifyHowdy,
420 /* .pfnNotifyBye = */ atsTcpNotifyBye,
421 /* .pfnNotifyReboot = */ atsTcpNotifyReboot,
422 /* .u32EndMarker = */ UINT32_C(0x12345678)
423};
424
Note: See TracBrowser for help on using the repository browser.

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