Changeset 68699 in vbox for trunk/src/VBox/Devices/Serial/DrvTCP.cpp
- Timestamp:
- Sep 7, 2017 3:12:54 PM (8 years ago)
- svn:sync-xref-src-repo-rev:
- 117930
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Serial/DrvTCP.cpp
r62956 r68699 5 5 6 6 /* 7 * Copyright (C) 2006-201 6Oracle Corporation.7 * Copyright (C) 2006-2017 Oracle Corporation. 8 8 * 9 9 * This file was contributed by Alexey Eromenko (derived from DrvNamedPipe) … … 28 28 #include <iprt/stream.h> 29 29 #include <iprt/alloc.h> 30 #include <iprt/pipe.h> 31 #include <iprt/poll.h> 30 32 #include <iprt/string.h> 31 33 #include <iprt/semaphore.h> 34 #include <iprt/socket.h> 35 #include <iprt/tcp.h> 32 36 #include <iprt/uuid.h> 33 37 #include <stdlib.h> 34 38 35 39 #include "VBoxDD.h" 36 37 #ifdef RT_OS_WINDOWS38 # include <iprt/win/ws2tcpip.h>39 #else /* !RT_OS_WINDOWS */40 # include <errno.h>41 # include <unistd.h>42 # include <sys/types.h>43 # include <sys/socket.h>44 # include <netinet/in.h>45 # include <netdb.h>46 # ifndef SHUT_RDWR /* OS/2 */47 # define SHUT_RDWR 348 # endif49 #endif /* !RT_OS_WINDOWS */50 51 #ifndef SHUT_RDWR52 # ifdef SD_BOTH53 # define SHUT_RDWR SD_BOTH54 # else55 # define SHUT_RDWR 256 # endif57 #endif58 59 40 60 41 /********************************************************************************************************************************* 61 42 * Defined Constants And Macros * 62 43 *********************************************************************************************************************************/ 63 /** Converts a pointer to DRVTCP::IMedia to a PDRVTCP. */ 64 #define PDMISTREAM_2_DRVTCP(pInterface) ( (PDRVTCP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTCP, IStream)) ) 65 44 45 #define DRVTCP_POLLSET_ID_SOCKET 0 46 #define DRVTCP_POLLSET_ID_WAKEUP 1 47 48 #define DRVTCP_WAKEUP_REASON_EXTERNAL 0 49 #define DRVTCP_WAKEUP_REASON_NEW_CONNECTION 1 66 50 67 51 /********************************************************************************************************************************* … … 84 68 bool fIsServer; 85 69 86 /** Socket handle of the TCP socket for server. */87 int TCPServer;70 /** Handle of the TCP server for incoming connections. */ 71 PRTTCPSERVER hTcpServ; 88 72 /** Socket handle of the TCP socket connection. */ 89 int TCPConnection; 73 RTSOCKET hTcpSock; 74 75 /** Poll set used to wait for I/O events. */ 76 RTPOLLSET hPollSet; 77 /** Reading end of the wakeup pipe. */ 78 RTPIPE hPipeWakeR; 79 /** Writing end of the wakeup pipe. */ 80 RTPIPE hPipeWakeW; 81 /** Flag whether the socket is in the pollset. */ 82 bool fTcpSockInPollSet; 90 83 91 84 /** Thread for listening for new connections. */ … … 101 94 102 95 96 /** 97 * Kicks any possibly polling thread to get informed about changes. 98 * 99 * @returns VBOx status code. 100 * @param pThis The TCP driver instance. 101 * @param bReason The reason code to handle. 102 */ 103 static int drvTcpPollerKick(PDRVTCP pThis, uint8_t bReason) 104 { 105 size_t cbWritten = 0; 106 return RTPipeWrite(pThis->hPipeWakeW, &bReason, 1, &cbWritten); 107 } 108 109 110 /** @interface_method_impl{PDMISTREAM,pfnPoll} */ 111 static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies) 112 { 113 int rc = VINF_SUCCESS; 114 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); 115 116 if (pThis->hTcpSock != NIL_RTSOCKET) 117 { 118 if (!pThis->fTcpSockInPollSet) 119 { 120 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, 121 fEvts, DRVTCP_POLLSET_ID_SOCKET); 122 if (RT_SUCCESS(rc)) 123 pThis->fTcpSockInPollSet = true; 124 } 125 else 126 { 127 /* Always include error event. */ 128 fEvts |= RTPOLL_EVT_ERROR; 129 rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts); 130 AssertRC(rc); 131 } 132 } 133 134 if (RT_SUCCESS(rc)) 135 { 136 while (RT_SUCCESS(rc)) 137 { 138 uint32_t fEvtsRecv = 0; 139 uint32_t idHnd = 0; 140 141 rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd); 142 if (RT_SUCCESS(rc)) 143 { 144 if (idHnd == DRVTCP_POLLSET_ID_WAKEUP) 145 { 146 /* We got woken up, drain the pipe and return. */ 147 uint8_t bReason; 148 size_t cbRead = 0; 149 rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead); 150 AssertRC(rc); 151 152 if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL) 153 rc = VERR_INTERRUPTED; 154 else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION) 155 { 156 Assert(!pThis->fTcpSockInPollSet); 157 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, 158 fEvts, DRVTCP_POLLSET_ID_SOCKET); 159 if (RT_SUCCESS(rc)) 160 pThis->fTcpSockInPollSet = true; 161 } 162 else 163 AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason)); 164 } 165 else 166 { 167 Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET); 168 169 /* On error we close the socket here. */ 170 if (fEvtsRecv & RTPOLL_EVT_ERROR) 171 { 172 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); 173 AssertRC(rc); 174 175 if (pThis->fIsServer) 176 RTTcpServerDisconnectClient2(pThis->hTcpSock); 177 else 178 RTSocketClose(pThis->hTcpSock); 179 pThis->hTcpSock = NIL_RTSOCKET; 180 pThis->fTcpSockInPollSet = false; 181 /* Continue with polling. */ 182 } 183 else 184 { 185 *pfEvts = fEvtsRecv; 186 break; 187 } 188 } 189 } 190 } 191 } 192 193 return rc; 194 } 195 196 197 /** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */ 198 static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface) 199 { 200 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); 201 return drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL); 202 } 203 204 103 205 /** @interface_method_impl{PDMISTREAM,pfnRead} */ 104 static DECLCALLBACK(int) drvT CPRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)206 static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead) 105 207 { 106 208 int rc = VINF_SUCCESS; 107 PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);209 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); 108 210 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation)); 109 211 110 212 Assert(pvBuf); 111 213 112 if (pThis-> TCPConnection != -1)113 { 114 s size_t cbReallyRead;115 unsigned cbBuf = (unsigned)*pcbRead;116 cbReallyRead = recv(pThis->TCPConnection, (char *)pvBuf, cbBuf, 0);117 if ( cbReallyRead == 0)214 if (pThis->hTcpSock != NIL_RTSOCKET) 215 { 216 size_t cbRead; 217 size_t cbBuf = *pcbRead; 218 rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead); 219 if (RT_SUCCESS(rc)) 118 220 { 119 int tmp = pThis->TCPConnection; 120 pThis->TCPConnection = -1; 121 #ifdef RT_OS_WINDOWS 122 closesocket(tmp); 123 #else 124 close(tmp); 125 #endif 221 if (!cbRead && rc != VINF_TRY_AGAIN) 222 { 223 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); 224 AssertRC(rc); 225 226 if (pThis->fIsServer) 227 RTTcpServerDisconnectClient2(pThis->hTcpSock); 228 else 229 RTSocketClose(pThis->hTcpSock); 230 pThis->hTcpSock = NIL_RTSOCKET; 231 pThis->fTcpSockInPollSet = false; 232 rc = VINF_SUCCESS; 233 } 234 *pcbRead = cbRead; 126 235 } 127 else if (cbReallyRead == -1)128 {129 cbReallyRead = 0;130 rc = RTErrConvertFromErrno(errno);131 }132 *pcbRead = cbReallyRead;133 236 } 134 237 else … … 144 247 145 248 /** @interface_method_impl{PDMISTREAM,pfnWrite} */ 146 static DECLCALLBACK(int) drvT CPWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)249 static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite) 147 250 { 148 251 int rc = VINF_SUCCESS; 149 PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);252 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); 150 253 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation)); 151 254 152 255 Assert(pvBuf); 153 if (pThis->TCPConnection != -1) 154 { 155 ssize_t cbWritten; 156 unsigned cbBuf = (unsigned)*pcbWrite; 157 cbWritten = send(pThis->TCPConnection, (const char *)pvBuf, cbBuf, 0); 158 if (cbWritten == 0) 159 { 160 int tmp = pThis->TCPConnection; 161 pThis->TCPConnection = -1; 162 #ifdef RT_OS_WINDOWS 163 closesocket(tmp); 164 #else 165 close(tmp); 166 #endif 167 } 168 else if (cbWritten == -1) 169 { 170 cbWritten = 0; 171 rc = RTErrConvertFromErrno(errno); 172 } 173 *pcbWrite = cbWritten; 174 } 256 if (pThis->hTcpSock != NIL_RTSOCKET) 257 { 258 size_t cbBuf = *pcbWrite; 259 rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite); 260 } 261 else 262 *pcbWrite = 0; 175 263 176 264 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc)); … … 205 293 RT_NOREF(hThreadSelf); 206 294 PDRVTCP pThis = (PDRVTCP)pvUser; 207 int rc;208 295 209 296 while (RT_LIKELY(!pThis->fShutdown)) 210 297 { 211 if (listen(pThis->TCPServer, 0) == -1) 298 RTSOCKET hTcpSockNew = NIL_RTSOCKET; 299 int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew); 300 if (RT_SUCCESS(rc)) 212 301 { 213 rc = RTErrConvertFromErrno(errno); 214 LogRel(("DrvTCP%d: listen failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc)); 215 break; 302 if (pThis->hTcpSock != NIL_RTSOCKET) 303 { 304 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance)); 305 RTTcpServerDisconnectClient2(hTcpSockNew); 306 } 307 else 308 { 309 pThis->hTcpSock = hTcpSockNew; 310 /* Inform the poller about the new socket. */ 311 drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION); 312 } 216 313 } 217 int s = accept(pThis->TCPServer, NULL, NULL);218 if (s == -1)219 {220 rc = RTErrConvertFromErrno(errno);221 LogRel(("DrvTCP%d: accept failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));222 break;223 }224 if (pThis->TCPConnection != -1)225 {226 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));227 #ifdef RT_OS_WINDOWS228 closesocket(s);229 #else230 close(s);231 #endif232 }233 else234 pThis->TCPConnection = s;235 314 } 236 315 … … 252 331 pThis->fShutdown = true; 253 332 if ( pThis->fIsServer 254 && pThis->TCPServer != -1) 255 { 256 int rc = shutdown(pThis->TCPServer, SHUT_RDWR); 257 AssertRC(rc == 0); NOREF(rc); 258 259 #ifdef RT_OS_WINDOWS 260 rc = closesocket(pThis->TCPServer); 261 #else 262 rc = close(pThis->TCPServer); 263 #endif 264 AssertRC(rc == 0); 265 pThis->TCPServer = -1; 333 && pThis->hTcpServ != NULL) 334 { 335 int rc = RTTcpServerShutdown(pThis->hTcpServ); 336 AssertRC(rc); 337 pThis->hTcpServ = NULL; 266 338 } 267 339 } … … 303 375 * While the thread exits, clean up as much as we can. 304 376 */ 305 306 Assert(pThis->TCPServer == -1); 307 if (pThis->TCPConnection != -1) 308 { 309 int rc = shutdown(pThis->TCPConnection, SHUT_RDWR); 310 AssertRC(rc == 0); NOREF(rc); 311 312 #ifdef RT_OS_WINDOWS 313 rc = closesocket(pThis->TCPConnection); 314 #else 315 rc = close(pThis->TCPConnection); 316 #endif 317 Assert(rc == 0); 318 pThis->TCPConnection = -1; 319 } 320 if ( pThis->fIsServer 321 && pThis->pszLocation) 322 RTFileDelete(pThis->pszLocation); 323 377 if (pThis->hTcpSock != NIL_RTSOCKET) 378 { 379 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); 380 AssertRC(rc); 381 382 rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */); 383 AssertRC(rc); 384 385 rc = RTSocketClose(pThis->hTcpSock); 386 AssertRC(rc); RT_NOREF(rc); 387 388 pThis->hTcpSock = NIL_RTSOCKET; 389 } 390 391 if (pThis->hPipeWakeR != NIL_RTPIPE) 392 { 393 int rc = RTPipeClose(pThis->hPipeWakeR); 394 AssertRC(rc); 395 396 pThis->hPipeWakeR = NIL_RTPIPE; 397 } 398 399 if (pThis->hPipeWakeW != NIL_RTPIPE) 400 { 401 int rc = RTPipeClose(pThis->hPipeWakeW); 402 AssertRC(rc); 403 404 pThis->hPipeWakeW = NIL_RTPIPE; 405 } 406 407 if (pThis->hPollSet != NIL_RTPOLLSET) 408 { 409 int rc = RTPollSetDestroy(pThis->hPollSet); 410 AssertRC(rc); 411 412 pThis->hPollSet = NIL_RTPOLLSET; 413 } 324 414 325 415 MMR3HeapFree(pThis->pszLocation); … … 337 427 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc)); 338 428 } 339 340 429 } 341 430 … … 351 440 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); 352 441 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP); 353 354 #ifdef RT_OS_WINDOWS355 {356 WSADATA wsaData;357 int err;358 359 err = WSAStartup(MAKEWORD(2,2), &wsaData);360 if (err != 0)361 {362 LogRel(("DrvTCP: Failed to initialize Winsock, error %d\n", err));363 /* XXX: let socket creation fail below */364 }365 }366 #endif367 442 368 443 /* … … 373 448 pThis->fIsServer = false; 374 449 375 pThis->TCPServer = -1; 376 pThis->TCPConnection = -1; 450 pThis->hTcpServ = NULL; 451 pThis->hTcpSock = NIL_RTSOCKET; 452 453 pThis->hPollSet = NIL_RTPOLLSET; 454 pThis->hPipeWakeR = NIL_RTPIPE; 455 pThis->hPipeWakeW = NIL_RTPIPE; 456 pThis->fTcpSockInPollSet = false; 377 457 378 458 pThis->ListenThread = NIL_RTTHREAD; … … 381 461 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface; 382 462 /* IStream */ 383 pThis->IStream.pfnRead = drvTCPRead; 384 pThis->IStream.pfnWrite = drvTCPWrite; 463 pThis->IStream.pfnPoll = drvTcpPoll; 464 pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt; 465 pThis->IStream.pfnRead = drvTcpRead; 466 pThis->IStream.pfnWrite = drvTcpWrite; 385 467 386 468 /* … … 398 480 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc); 399 481 482 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */); 483 if (RT_FAILURE(rc)) 484 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 485 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance); 486 487 rc = RTPollSetCreate(&pThis->hPollSet); 488 if (RT_FAILURE(rc)) 489 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 490 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance); 491 492 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR, 493 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 494 DRVTCP_POLLSET_ID_WAKEUP); 495 if (RT_FAILURE(rc)) 496 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 497 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"), 498 pDrvIns->iInstance, pThis->pszLocation); 499 400 500 /* 401 501 * Create/Open the socket. 402 502 */ 403 int s = socket(PF_INET, SOCK_STREAM, 0);404 if (s == -1)405 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,406 N_("DrvTCP#%d failed to create socket"), pDrvIns->iInstance);407 408 struct sockaddr_in addr;409 memset(&addr, 0, sizeof(addr));410 addr.sin_family = AF_INET;411 412 503 if (pThis->fIsServer) 413 504 { 414 addr.sin_addr.s_addr = INADDR_ANY; 415 addr.sin_port = htons(/*PORT*/ atoi(pThis->pszLocation)); 416 417 /* Bind address to the telnet socket. */ 418 pThis->TCPServer = s; 419 RTFileDelete(pThis->pszLocation); 420 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) 421 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, 422 N_("DrvTCP#%d failed to bind to socket %s"), 423 pDrvIns->iInstance, pThis->pszLocation); 505 uint32_t uPort = 0; 506 rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort); 507 if (RT_FAILURE(rc)) 508 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 509 N_("DrvTCP#%d: The port part of the location is not a numerical value"), 510 pDrvIns->iInstance); 511 512 /** @todo: Allow binding to distinct interfaces. */ 513 rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ); 514 if (RT_FAILURE(rc)) 515 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 516 N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance); 517 424 518 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0, 425 519 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream"); … … 430 524 else 431 525 { 432 char *token; 433 const char *delim = ":"; 434 struct hostent *server; 435 token = strtok(pThis->pszLocation, delim); 436 if(token) { 437 server = gethostbyname(token); 438 memmove((char *)&addr.sin_addr.s_addr, 439 (char *)server->h_addr, 440 server->h_length); 441 } 442 token = strtok(NULL, delim); 443 if(token) { 444 addr.sin_port = htons(/*PORT*/ atoi(token)); 445 } 446 447 /* Connect to the telnet socket. */ 448 pThis->TCPConnection = s; 449 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) 450 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, 526 char *pszPort = strchr(pThis->pszLocation, ':'); 527 if (!pszPort) 528 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS, 529 N_("DrvTCP#%d: The location misses the port to connect to"), 530 pDrvIns->iInstance); 531 532 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */ 533 uint32_t uPort = 0; 534 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort); 535 if (RT_FAILURE(rc)) 536 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 537 N_("DrvTCP#%d: The port part of the location is not a numerical value"), 538 pDrvIns->iInstance); 539 540 rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock); 541 *pszPort = ':'; /* Restore delimiter before checking the status. */ 542 if (RT_FAILURE(rc)) 543 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 451 544 N_("DrvTCP#%d failed to connect to socket %s"), 452 545 pDrvIns->iInstance, pThis->pszLocation); 546 547 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, 548 RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR, 549 DRVTCP_POLLSET_ID_SOCKET); 550 if (RT_FAILURE(rc)) 551 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, 552 N_("DrvTCP#%d failed to add socket for %s to poll set"), 553 pDrvIns->iInstance, pThis->pszLocation); 554 555 pThis->fTcpSockInPollSet = true; 453 556 } 454 557
Note:
See TracChangeset
for help on using the changeset viewer.