VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/tcp.cpp@ 5938

Last change on this file since 5938 was 5938, checked in by vboxsync, 17 years ago

iprt: small performance fix using the optimal fd number for select, and a slight cosmetic 101% correctness fix for the socket creation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.3 KB
Line 
1/* $Id: tcp.cpp 5938 2007-12-03 09:57:50Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - TCP/IP.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#ifdef RT_OS_WINDOWS
23#include <winsock.h>
24#else /* !RT_OS_WINDOWS */
25#include <errno.h>
26#include <sys/stat.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#include <arpa/inet.h>
31#include <sys/un.h>
32#include <netdb.h>
33#include <unistd.h>
34#endif /* !RT_OS_WINDOWS */
35
36#include <iprt/tcp.h>
37#include <iprt/thread.h>
38#include <iprt/alloc.h>
39#include <iprt/assert.h>
40#include <iprt/asm.h>
41#include <iprt/err.h>
42#include <iprt/string.h>
43
44
45/* non-standard linux stuff (it seems). */
46#ifndef MSG_NOSIGNAL
47# define MSG_NOSIGNAL 0
48#endif
49#ifndef SHUT_RDWR
50# ifdef SD_BOTH
51# define SHUT_RDWR SD_BOTH
52# else
53# define SHUT_RDWR 2
54# endif
55#endif
56
57/* fixup backlevel OSes. */
58#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
59# define socklen_t int
60#endif
61
62
63/*******************************************************************************
64* Defined Constants And Macros *
65*******************************************************************************/
66#define BACKLOG 10 /* how many pending connections queue will hold */
67
68
69/*******************************************************************************
70* Structures and Typedefs *
71*******************************************************************************/
72/**
73 * TCP Server state.
74 */
75typedef enum RTTCPSERVERSTATE
76{
77 /** Invalid. */
78 RTTCPSERVERSTATE_INVALID = 0,
79 /** Created. */
80 RTTCPSERVERSTATE_CREATED,
81 /** Listener thread is starting up. */
82 RTTCPSERVERSTATE_STARTING,
83 /** Accepting client connections. */
84 RTTCPSERVERSTATE_ACCEPTING,
85 /** Serving a client. */
86 RTTCPSERVERSTATE_SERVING,
87 /** Listener terminating. */
88 RTTCPSERVERSTATE_STOPPING,
89 /** Listener terminated. */
90 RTTCPSERVERSTATE_STOPPED,
91 /** Destroying signaling to the listener, listener will wait. */
92 RTTCPSERVERSTATE_SIGNALING,
93 /** Listener cleans up. */
94 RTTCPSERVERSTATE_DESTROYING,
95 /** Freed. */
96 RTTCPSERVERSTATE_FREED
97} RTTCPSERVERSTATE;
98
99/*
100 * Internal representation of the TCP Server handle.
101 */
102typedef struct RTTCPSERVER
103{
104 /** The server state. */
105 RTTCPSERVERSTATE volatile enmState;
106 /** The server thread. */
107 RTTHREAD Thread;
108 /** The server socket. */
109 RTSOCKET volatile SockServer;
110 /** The socket to the client currently being serviced.
111 * This is NIL_RTSOCKET when no client is serviced. */
112 RTSOCKET volatile SockClient;
113 /** The connection function. */
114 PFNRTTCPSERVE pfnServe;
115 /** Argument to pfnServer. */
116 void *pvUser;
117} RTTCPSERVER;
118
119
120/*******************************************************************************
121* Internal Functions *
122*******************************************************************************/
123static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer);
124static int rtTcpServerListen(PRTTCPSERVER pServer);
125static void rcTcpServerListenCleanup(PRTTCPSERVER pServer);
126static void rtTcpServerDestroyServerSock(RTSOCKET SockServer, const char *pszMsg);
127static int rtTcpClose(RTSOCKET Sock, const char *pszMsg);
128
129
130
131/**
132 * Get the last error as an iprt status code.
133 * @returns iprt status code.
134 */
135DECLINLINE(int) rtTcpError(void)
136{
137#ifdef RT_OS_WINDOWS
138 return RTErrConvertFromWin32(WSAGetLastError());
139#else
140 return RTErrConvertFromErrno(errno);
141#endif
142}
143
144
145/**
146 * Atomicly updates a socket variable.
147 * @returns The old value.
148 * @param pSock The socket variable to update.
149 * @param Sock The new value.
150 */
151DECLINLINE(RTSOCKET) rtTcpAtomicXchgSock(RTSOCKET volatile *pSock, const RTSOCKET Sock)
152{
153 switch (sizeof(RTSOCKET))
154 {
155 case 4: return (RTSOCKET)ASMAtomicXchgS32((int32_t volatile *)pSock, (int32_t)Sock);
156 default:
157 AssertReleaseFailed();
158 return NIL_RTSOCKET;
159 }
160}
161
162
163/**
164 * Changes the TCP server state.
165 */
166DECLINLINE(bool) rtTcpServerSetState(PRTTCPSERVER pServer, RTTCPSERVERSTATE enmStateNew, RTTCPSERVERSTATE enmStateOld)
167{
168 bool fRc;
169 ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
170 return fRc;
171}
172
173
174/**
175 * Create single connection at a time TCP Server in a separate thread.
176 *
177 * The thread will loop accepting connections and call pfnServe for
178 * each of the incoming connections in turn. The pfnServe function can
179 * return VERR_TCP_SERVER_STOP too terminate this loop. RTTcpServerDestroy()
180 * should be used to terminate the server.
181 *
182 * @returns iprt status code.
183 * @param pszAddress The address for creating a listening socket.
184 * If NULL or empty string the server is bound to all interfaces.
185 * @param uPort The port for creating a listening socket.
186 * @param enmType The thread type.
187 * @param pszThrdName The name of the worker thread.
188 * @param pfnServe The function which will serve a new client connection.
189 * @param pvUser User argument passed to pfnServe.
190 * @param ppServer Where to store the serverhandle.
191 */
192RTR3DECL(int) RTTcpServerCreate(const char *pszAddress, unsigned uPort, RTTHREADTYPE enmType, const char *pszThrdName,
193 PFNRTTCPSERVE pfnServe, void *pvUser, PPRTTCPSERVER ppServer)
194{
195 /*
196 * Do params checking
197 */
198 if (!uPort || !pfnServe || !pszThrdName || !ppServer)
199 {
200 AssertMsgFailed(("Invalid params\n"));
201 return VERR_INVALID_PARAMETER;
202 }
203
204 /*
205 * Create the server.
206 */
207 PRTTCPSERVER pServer;
208 int rc = RTTcpServerCreateEx(pszAddress, uPort, &pServer);
209 if (RT_SUCCESS(rc))
210 {
211 /*
212 * Create the listener thread.
213 */
214 pServer->enmState = RTTCPSERVERSTATE_STARTING;
215 pServer->pvUser = pvUser;
216 pServer->pfnServe = pfnServe;
217 rc = RTThreadCreate(&pServer->Thread, rtTcpServerThread, pServer, 0, enmType, /*RTTHREADFLAGS_WAITABLE*/0, pszThrdName);
218 if (RT_SUCCESS(rc))
219 {
220 /* done */
221 if (ppServer)
222 *ppServer = pServer;
223 return rc;
224 }
225
226 /*
227 * Destroy the server.
228 */
229 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_CREATED, RTTCPSERVERSTATE_STARTING);
230 RTTcpServerDestroy(pServer);
231 }
232
233 return rc;
234}
235
236
237/**
238 * Server thread, loops accepting connections until it's terminated.
239 *
240 * @returns iprt status code. (ignored).
241 * @param ThreadSelf Thread handle.
242 * @param pvServer Server handle.
243 */
244static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer)
245{
246 PRTTCPSERVER pServer = (PRTTCPSERVER)pvServer;
247 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_STARTING))
248 return rtTcpServerListen(pServer);
249 rcTcpServerListenCleanup(pServer);
250 NOREF(ThreadSelf);
251 return VINF_SUCCESS;
252}
253
254
255/**
256 * Create single connection at a time TCP Server.
257 * The caller must call RTTcpServerListen() to actually start the server.
258 *
259 * @returns iprt status code.
260 * @param pszAddress The address for creating a listening socket.
261 * If NULL the server is bound to all interfaces.
262 * @param uPort The port for creating a listening socket.
263 * @param ppServer Where to store the serverhandle.
264 */
265RTR3DECL(int) RTTcpServerCreateEx(const char *pszAddress, uint32_t uPort, PPRTTCPSERVER ppServer)
266{
267 int rc;
268
269 /*
270 * Do params checking
271 */
272 if (!uPort || !ppServer)
273 {
274 AssertMsgFailed(("Invalid params\n"));
275 return VERR_INVALID_PARAMETER;
276 }
277
278#ifdef RT_OS_WINDOWS
279 /*
280 * Initialize WinSock and check version.
281 */
282 WORD wVersionRequested = MAKEWORD(1, 1);
283 WSADATA wsaData;
284 rc = WSAStartup(wVersionRequested, &wsaData);
285 if (wsaData.wVersion != wVersionRequested)
286 {
287 AssertMsgFailed(("Wrong winsock version\n"));
288 return VERR_NOT_SUPPORTED;
289 }
290#endif
291
292 /*
293 * Get host listening address.
294 */
295 struct hostent *pHostEnt = NULL;
296 if (pszAddress != NULL && *pszAddress)
297 {
298 pHostEnt = gethostbyname(pszAddress);
299 if (!pHostEnt)
300 {
301 struct in_addr InAddr;
302 InAddr.s_addr = inet_addr(pszAddress);
303 pHostEnt = gethostbyaddr((char *)&InAddr, 4, AF_INET);
304 if (!pHostEnt)
305 {
306 rc = rtTcpError();
307 AssertMsgFailed(("Could not get host address rc=%Vrc\n", rc));
308 return rc;
309 }
310 }
311 }
312
313 /*
314 * Setting up socket.
315 */
316 RTSOCKET WaitSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
317 if (WaitSock != -1)
318 {
319 /*
320 * Set socket options.
321 */
322 int fFlag = 1;
323 if (!setsockopt(WaitSock, SOL_SOCKET, SO_REUSEADDR, (const char *)&fFlag, sizeof(fFlag)))
324 {
325 /*
326 * Set socket family, address and port.
327 */
328 struct sockaddr_in LocalAddr = {0};
329 LocalAddr.sin_family = AF_INET;
330 LocalAddr.sin_port = htons(uPort);
331 /* if address not specified, use INADDR_ANY. */
332 if (!pHostEnt)
333 LocalAddr.sin_addr.s_addr = INADDR_ANY;
334 else
335 LocalAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr);
336
337 /*
338 * Bind a name to a socket.
339 */
340 if (bind(WaitSock, (struct sockaddr *)&LocalAddr, sizeof(LocalAddr)) != -1)
341 {
342 /*
343 * Listen for connections on a socket.
344 */
345 if (listen(WaitSock, BACKLOG) != -1)
346 {
347 /*
348 * Create the server handle.
349 */
350 PRTTCPSERVER pServer = (PRTTCPSERVER)RTMemAllocZ(sizeof(*pServer));
351 if (pServer)
352 {
353 pServer->SockServer = WaitSock;
354 pServer->SockClient = NIL_RTSOCKET;
355 pServer->Thread = NIL_RTTHREAD;
356 pServer->enmState = RTTCPSERVERSTATE_CREATED;
357 *ppServer = pServer;
358 return VINF_SUCCESS;
359 }
360 else
361 rc = VERR_NO_MEMORY;
362 }
363 else
364 {
365 rc = rtTcpError();
366 AssertMsgFailed(("listen() %Vrc\n", rc));
367 }
368 }
369 else
370 {
371 rc = rtTcpError();
372 }
373 }
374 else
375 {
376 rc = rtTcpError();
377 AssertMsgFailed(("setsockopt() %Vrc\n", rc));
378 }
379 rtTcpClose(WaitSock, "RTServerCreateEx");
380 }
381 else
382 {
383 rc = rtTcpError();
384 AssertMsgFailed(("socket() %Vrc\n", rc));
385 }
386
387 return rc;
388}
389
390
391/**
392 * Listen for incoming connections.
393 *
394 * The function will loop accepting connections and call pfnServe for
395 * each of the incoming connections in turn. The pfnServe function can
396 * return VERR_TCP_SERVER_STOP too terminate this loop. A stopped server
397 * can only be destroyed.
398 *
399 * @returns iprt status code.
400 * @param pServer The server handle as returned from RTTcpServerCreateEx().
401 * @param pfnServe The function which will serve a new client connection.
402 * @param pvUser User argument passed to pfnServe.
403 */
404RTR3DECL(int) RTTcpServerListen(PRTTCPSERVER pServer, PFNRTTCPSERVE pfnServe, void *pvUser)
405{
406 /*
407 * Validate input.
408 */
409 if (!pfnServe || !pServer)
410 {
411 AssertMsgFailed(("pfnServer=%p pServer=%p\n", pfnServe, pServer));
412 return VERR_INVALID_PARAMETER;
413 }
414 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_CREATED))
415 {
416 Assert(!pServer->pfnServe);
417 Assert(!pServer->pvUser);
418 Assert(pServer->Thread == NIL_RTTHREAD);
419 Assert(pServer->SockClient == NIL_RTSOCKET);
420
421 pServer->pfnServe = pfnServe;
422 pServer->pvUser = pvUser;
423 pServer->Thread = RTThreadSelf();
424 Assert(pServer->Thread != NIL_RTTHREAD);
425 return rtTcpServerListen(pServer);
426 }
427 AssertMsgFailed(("pServer->enmState=%d\n", pServer->enmState));
428 return VERR_INVALID_PARAMETER;
429}
430
431
432/**
433 * Closes the client socket.
434 */
435static int rtTcpServerDestroyClientSock(RTSOCKET volatile *pSock, const char *pszMsg)
436{
437 RTSOCKET Sock = rtTcpAtomicXchgSock(pSock, NIL_RTSOCKET);
438 if (Sock != NIL_RTSOCKET)
439 shutdown(Sock, SHUT_RDWR);
440 return rtTcpClose(Sock, pszMsg);
441}
442
443
444/**
445 * Internal worker common for RTTcpServerListen and the thread created by RTTcpServerCreate().
446 */
447static int rtTcpServerListen(PRTTCPSERVER pServer)
448{
449 /*
450 * Accept connection loop.
451 */
452 int rc = VINF_SUCCESS;
453 for (;;)
454 {
455 /*
456 * Change state.
457 */
458 RTTCPSERVERSTATE enmState = pServer->enmState;
459 if ( enmState != RTTCPSERVERSTATE_ACCEPTING
460 && enmState != RTTCPSERVERSTATE_SERVING)
461 break;
462 if (!rtTcpServerSetState(pServer, RTTCPSERVERSTATE_ACCEPTING, enmState))
463 continue;
464
465 /*
466 * Accept connection.
467 */
468 struct sockaddr_in RemoteAddr = {0};
469 socklen_t Len = sizeof(RemoteAddr);
470 RTSOCKET Socket = accept(pServer->SockServer, (struct sockaddr *)&RemoteAddr, &Len);
471 if (Socket == -1)
472 {
473#ifndef RT_OS_WINDOWS
474 /* These are typical for what can happen during destruction. */
475 if (errno == EBADF || errno == EINVAL || errno == ENOTSOCK)
476 break;
477#endif
478 continue;
479 }
480
481 /*
482 * Run a pfnServe callback.
483 */
484 if (!rtTcpServerSetState(pServer, RTTCPSERVERSTATE_SERVING, RTTCPSERVERSTATE_ACCEPTING))
485 break;
486 rtTcpAtomicXchgSock(&pServer->SockClient, Socket);
487 rc = pServer->pfnServe(Socket, pServer->pvUser);
488 rtTcpServerDestroyClientSock(&pServer->SockClient, "Listener: client");
489
490 /*
491 * Stop the server?
492 */
493 if (rc == VERR_TCP_SERVER_STOP)
494 {
495 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPING, RTTCPSERVERSTATE_SERVING))
496 {
497 /*
498 * Reset the server socket and change the state to stopped. After that state change
499 * we cannot safely access the handle so we'll have to return here.
500 */
501 RTSOCKET SockServer = rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET);
502 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPED, RTTCPSERVERSTATE_STOPPING);
503 rtTcpClose(SockServer, "Listener: server stopped");
504 return rc;
505 }
506 break;
507 }
508 }
509
510 /*
511 * Perform any pending clean and be gone.
512 */
513 rcTcpServerListenCleanup(pServer);
514 return rc;
515}
516
517
518/**
519 * Clean up after listener.
520 */
521static void rcTcpServerListenCleanup(PRTTCPSERVER pServer)
522{
523 /*
524 * Wait for any destroyers to finish signaling us.
525 */
526 for (unsigned cTries = 99; cTries > 0; cTries--)
527 {
528 RTTCPSERVERSTATE enmState = pServer->enmState;
529 switch (enmState)
530 {
531 /*
532 * Intermediate state while the destroyer closes the client socket.
533 */
534 case RTTCPSERVERSTATE_SIGNALING:
535 if (!RTThreadYield())
536 RTThreadSleep(1);
537 break;
538
539 /*
540 * Free the handle.
541 */
542 case RTTCPSERVERSTATE_DESTROYING:
543 {
544 rtTcpClose(rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET), "Listener-cleanup: server");
545 rtTcpServerSetState(pServer, RTTCPSERVERSTATE_FREED, RTTCPSERVERSTATE_DESTROYING);
546 RTMemFree(pServer);
547 return;
548 }
549
550 /*
551 * Everything else means failure.
552 */
553 default:
554 AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
555 return;
556 }
557 }
558 AssertMsgFailed(("Timed out when trying to clean up after listener. pServer=%p enmState=%d\n", pServer, pServer->enmState));
559}
560
561
562/**
563 * Terminate the open connection to the server.
564 *
565 * @returns iprt status code.
566 * @param pServer Handle to the server.
567 */
568RTR3DECL(int) RTTcpServerDisconnectClient(PRTTCPSERVER pServer)
569{
570 /*
571 * Validate input.
572 */
573 if ( !pServer
574 || pServer->enmState <= RTTCPSERVERSTATE_INVALID
575 || pServer->enmState >= RTTCPSERVERSTATE_FREED)
576 {
577 AssertMsgFailed(("Invalid parameter!\n"));
578 return VERR_INVALID_PARAMETER;
579 }
580
581 return rtTcpServerDestroyClientSock(&pServer->SockClient, "DisconnectClient: client");
582}
583
584
585/**
586 * Closes down and frees a TCP Server.
587 * This will also terminate any open connections to the server.
588 *
589 * @returns iprt status code.
590 * @param pServer Handle to the server.
591 */
592RTR3DECL(int) RTTcpServerDestroy(PRTTCPSERVER pServer)
593{
594 /*
595 * Validate input.
596 */
597 if ( !pServer
598 || pServer->enmState <= RTTCPSERVERSTATE_INVALID
599 || pServer->enmState >= RTTCPSERVERSTATE_FREED)
600 {
601 AssertMsgFailed(("Invalid parameter!\n"));
602 return VERR_INVALID_PARAMETER;
603 }
604
605/** @todo r=bird: Some of this horrible code can probably be exchanged with a RTThreadWait(). (It didn't exist when this code was written.) */
606
607 /*
608 * Move it to the destroying state.
609 */
610 RTSOCKET SockServer = rtTcpAtomicXchgSock(&pServer->SockServer, NIL_RTSOCKET);
611 for (unsigned cTries = 99; cTries > 0; cTries--)
612 {
613 RTTCPSERVERSTATE enmState = pServer->enmState;
614 switch (enmState)
615 {
616 /*
617 * Try move it to the destroying state.
618 */
619 case RTTCPSERVERSTATE_STARTING:
620 case RTTCPSERVERSTATE_ACCEPTING:
621 case RTTCPSERVERSTATE_SERVING:
622 {
623 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_SIGNALING, enmState))
624 {
625 /* client */
626 rtTcpServerDestroyClientSock(&pServer->SockClient, "Destroyer: client");
627
628 bool fRc = rtTcpServerSetState(pServer, RTTCPSERVERSTATE_DESTROYING, RTTCPSERVERSTATE_SIGNALING);
629 Assert(fRc); NOREF(fRc);
630
631 /* server */
632 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server destroying");
633 RTThreadYield();
634
635 return VINF_SUCCESS;
636 }
637 break;
638 }
639
640
641 /*
642 * Intermediate state.
643 */
644 case RTTCPSERVERSTATE_STOPPING:
645 if (!RTThreadYield())
646 RTThreadSleep(1);
647 break;
648
649 /*
650 * Just release the handle.
651 */
652 case RTTCPSERVERSTATE_CREATED:
653 case RTTCPSERVERSTATE_STOPPED:
654 if (rtTcpServerSetState(pServer, RTTCPSERVERSTATE_FREED, enmState))
655 {
656 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server freeing");
657 RTMemFree(pServer);
658 return VINF_TCP_SERVER_STOP;
659 }
660 break;
661
662 /*
663 * Everything else means failure.
664 */
665 default:
666 AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
667 return VERR_INTERNAL_ERROR;
668 }
669 }
670
671 AssertMsgFailed(("Giving up! pServer=%p enmState=%d\n", pServer, pServer->enmState));
672 rtTcpServerDestroyServerSock(SockServer, "Destroyer: server timeout");
673 return VERR_INTERNAL_ERROR;
674}
675
676
677/**
678 * Shutdowns the server socket.
679 */
680static void rtTcpServerDestroyServerSock(RTSOCKET SockServer, const char *pszMsg)
681{
682 if (SockServer == NIL_RTSOCKET)
683 return;
684 shutdown(SockServer, SHUT_RDWR);
685 rtTcpClose(SockServer, "Destroyer: server destroying");
686}
687
688
689
690RTR3DECL(int) RTTcpRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
691{
692 /*
693 * Do params checking
694 */
695 if (!pvBuffer || !cbBuffer)
696 {
697 AssertMsgFailed(("Invalid params\n"));
698 return VERR_INVALID_PARAMETER;
699 }
700
701 /*
702 * Read loop.
703 * If pcbRead is NULL we have to fill the entire buffer!
704 */
705 size_t cbRead = 0;
706 size_t cbToRead = cbBuffer;
707 for (;;)
708 {
709 ssize_t cbBytesRead = recv(Sock, (char *)pvBuffer + cbRead, cbToRead, MSG_NOSIGNAL);
710 if (cbBytesRead < 0)
711 return rtTcpError();
712 if (cbBytesRead == 0 && rtTcpError())
713 return rtTcpError();
714 if (pcbRead)
715 {
716 /* return partial data */
717 *pcbRead = cbBytesRead;
718 break;
719 }
720
721 /* read more? */
722 cbRead += cbBytesRead;
723 if (cbRead == cbBuffer)
724 break;
725
726 /* next */
727 cbToRead = cbBuffer - cbRead;
728 }
729
730 return VINF_SUCCESS;
731}
732
733
734RTR3DECL(int) RTTcpWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
735{
736 do
737 {
738 ssize_t cbWritten = send(Sock, (const char *)pvBuffer, cbBuffer, MSG_NOSIGNAL);
739 if (cbWritten < 0)
740 return rtTcpError();
741 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d rtTcpError()=%d\n",
742 cbWritten, cbBuffer, rtTcpError()));
743 cbBuffer -= cbWritten;
744 pvBuffer = (char *)pvBuffer + cbWritten;
745 } while (cbBuffer);
746
747 return VINF_SUCCESS;
748}
749
750
751RTR3DECL(int) RTTcpFlush(RTSOCKET Sock)
752{
753 int fFlag = 1;
754 setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&fFlag, sizeof(fFlag));
755 fFlag = 0;
756 setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&fFlag, sizeof(fFlag));
757
758 return VINF_SUCCESS;
759}
760
761
762RTR3DECL(int) RTTcpSelectOne(RTSOCKET Sock, unsigned cMillies)
763{
764 fd_set fdsetR;
765 FD_ZERO(&fdsetR);
766 FD_SET(Sock, &fdsetR);
767
768 fd_set fdsetE = fdsetR;
769
770 int rc;
771 if (cMillies == RT_INDEFINITE_WAIT)
772 rc = select(Sock + 1, &fdsetR, NULL, &fdsetE, NULL);
773 else
774 {
775 struct timeval timeout;
776 timeout.tv_sec = cMillies / 1000;
777 timeout.tv_usec = (cMillies % 1000) * 1000;
778 rc = select(Sock + 1, &fdsetR, NULL, &fdsetE, &timeout);
779 }
780 if (rc > 0)
781 return VINF_SUCCESS;
782 if (rc == 0)
783 return VERR_TIMEOUT;
784 return rtTcpError();
785}
786
787
788RTR3DECL(int) RTTcpClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
789{
790 int rc;
791
792 /*
793 * Do params checking
794 */
795 AssertReturn(uPort, VERR_INVALID_PARAMETER);
796 AssertReturn(VALID_PTR(pszAddress), VERR_INVALID_PARAMETER);
797
798#ifdef RT_OS_WINDOWS
799 /*
800 * Initialize WinSock and check version.
801 */
802 WORD wVersionRequested = MAKEWORD(1, 1);
803 WSADATA wsaData;
804 rc = WSAStartup(wVersionRequested, &wsaData);
805 if (wsaData.wVersion != wVersionRequested)
806 {
807 AssertMsgFailed(("Wrong winsock version\n"));
808 return VERR_NOT_SUPPORTED;
809 }
810#endif
811
812 /*
813 * Resolve the address.
814 */
815 struct hostent *pHostEnt = NULL;
816 pHostEnt = gethostbyname(pszAddress);
817 if (!pHostEnt)
818 {
819 struct in_addr InAddr;
820 InAddr.s_addr = inet_addr(pszAddress);
821 pHostEnt = gethostbyaddr((char *)&InAddr, 4, AF_INET);
822 if (!pHostEnt)
823 {
824 rc = rtTcpError();
825 AssertMsgFailed(("Could not resolve '%s', rc=%Vrc\n", pszAddress, rc));
826 return rc;
827 }
828 }
829
830 /*
831 * Create the socket and connect.
832 */
833 RTSOCKET Sock = socket(PF_INET, SOCK_STREAM, 0);
834 if (Sock != -1)
835 {
836 struct sockaddr_in InAddr = {0};
837 InAddr.sin_family = AF_INET;
838 InAddr.sin_port = htons(uPort);
839 InAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr);
840 if (!connect(Sock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
841 {
842 *pSock = Sock;
843 return VINF_SUCCESS;
844 }
845 rc = rtTcpError();
846 rtTcpClose(Sock, "RTTcpClientConnect");
847 }
848 else
849 rc = rtTcpError();
850 return rc;
851}
852
853
854RTR3DECL(int) RTTcpClientClose(RTSOCKET Sock)
855{
856 return rtTcpClose(Sock, "RTTcpClientClose");
857}
858
859
860/**
861 * Internal close function which does all the proper bitching.
862 */
863static int rtTcpClose(RTSOCKET Sock, const char *pszMsg)
864{
865 /* ignore nil handles. */
866 if (Sock == NIL_RTSOCKET)
867 return VINF_SUCCESS;
868
869 /*
870 * Attempt to close it.
871 */
872#ifdef RT_OS_WINDOWS
873 int rc = closesocket(Sock);
874#else
875 int rc = close(Sock);
876#endif
877 if (!rc)
878 return VINF_SUCCESS;
879 rc = rtTcpError();
880 AssertMsgFailed(("\"%s\": close(%d) -> %Vrc\n", pszMsg, Sock, rc));
881 return rc;
882}
883
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