aboutsummaryrefslogtreecommitdiff
path: root/src/tcpsocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tcpsocket.cpp')
-rw-r--r--src/tcpsocket.cpp450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/tcpsocket.cpp b/src/tcpsocket.cpp
new file mode 100644
index 0000000..bbd9cf5
--- /dev/null
+++ b/src/tcpsocket.cpp
@@ -0,0 +1,450 @@
1/*
2 * Copyright (C) 2007-2010 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#include <string.h>
9#include <stdio.h>
10#include <errno.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <sys/types.h>
14#include <sys/time.h>
15#include <errno.h>
16#include <fcntl.h>
17#include "bu/tcpsocket.h"
18
19#include "bu/config.h"
20
21#ifndef WIN32
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <arpa/inet.h>
26#else
27 #include <Winsock2.h>
28#endif
29
30#define RBS (1024*2)
31
32namespace Bu { subExceptionDef( TcpSocketException ) }
33
34Bu::TcpSocket::TcpSocket( int nTcpSocket ) :
35 nTcpSocket( nTcpSocket ),
36 bActive( true ),
37 bBlocking( true )
38{
39#ifdef WIN32
40 Bu::Winsock2::getInstance();
41#endif
42 setAddress();
43}
44
45Bu::TcpSocket::TcpSocket( const Bu::FString &sAddr, int nPort, int nTimeout,
46 bool bBlocking ) :
47 nTcpSocket( 0 ),
48 bActive( false ),
49 bBlocking( true )
50{
51#ifdef WIN32
52 Bu::Winsock2::getInstance();
53#endif
54
55 /* Create the socket. */
56 nTcpSocket = bu_socket( PF_INET, SOCK_STREAM, 0 );
57
58 if( nTcpSocket < 0 )
59 {
60 throw ExceptionBase("Couldn't create socket.\n");
61 }
62
63 setBlocking( false );
64
65 /* Connect to the server. */
66 //printf("Resolving hostname (%s)...\n", sAddr );
67 {
68 struct addrinfo *pAddr = NULL;
69 struct addrinfo aiHints;
70 memset( &aiHints, 0, sizeof(addrinfo) );
71 aiHints.ai_flags = AI_CANONNAME;
72 aiHints.ai_family = AF_INET;
73 aiHints.ai_socktype = SOCK_STREAM;
74 char ibuf[10];
75 sprintf( ibuf, "%d", nPort );
76
77 int ret;
78 if( (ret = bu_getaddrinfo(
79 sAddr.getStr(), ibuf, &aiHints, &pAddr )) != 0 )
80 {
81 close();
82 throw Bu::TcpSocketException("Couldn't resolve hostname %s (%s).\n",
83 sAddr.getStr(), bu_gai_strerror(ret));
84 }
85
86 bu_connect(
87 nTcpSocket,
88 pAddr->ai_addr,
89 pAddr->ai_addrlen
90 );
91
92 sAddress = pAddr->ai_canonname;
93
94 bu_freeaddrinfo( pAddr );
95 }
96
97 bActive = true;
98
99 if( nTimeout > 0 )
100 {
101 fd_set rfds, wfds, efds;
102 int retval;
103
104 FD_ZERO(&rfds);
105 FD_SET(nTcpSocket, &rfds);
106 FD_ZERO(&wfds);
107 FD_SET(nTcpSocket, &wfds);
108 FD_ZERO(&efds);
109 FD_SET(nTcpSocket, &efds);
110
111 struct timeval tv;
112 tv.tv_sec = nTimeout;
113 tv.tv_usec = 0;
114
115 retval = bu_select( nTcpSocket+1, &rfds, &wfds, &efds, &tv );
116
117 if( retval == 0 )
118 {
119 close();
120 throw ExceptionBase("Connection timeout.\n");
121 }
122 read( NULL, 0 ); // See if we can get any errors out of the way early.
123 }
124
125 if( bBlocking )
126 setBlocking( bBlocking );
127}
128
129Bu::TcpSocket::~TcpSocket()
130{
131 close();
132}
133
134void Bu::TcpSocket::close()
135{
136 if( bActive )
137 {
138#ifndef WIN32
139 fsync( nTcpSocket );
140#endif
141#ifdef WIN32
142 #ifndef SHUT_RDWR
143 #define SHUT_RDWR (SD_BOTH)
144 #endif
145#endif
146 bu_shutdown( nTcpSocket, SHUT_RDWR );
147 ::close( nTcpSocket );
148 }
149 bActive = false;
150}
151
152size_t Bu::TcpSocket::read( void *pBuf, size_t nBytes )
153{
154 fd_set rfds;
155 FD_ZERO(&rfds);
156 FD_SET(nTcpSocket, &rfds);
157 struct timeval tv = {0, 0};
158 if( bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv ) < 0 )
159 {
160 int iErr = errno;
161 close();
162 throw TcpSocketException( TcpSocketException::cRead, strerror(iErr) );
163 }
164 if( FD_ISSET( nTcpSocket, &rfds ) || bBlocking )
165 {
166 int nRead = TEMP_FAILURE_RETRY(
167 bu_recv( nTcpSocket, (char *) pBuf, nBytes, 0 ) );
168 if( nRead == 0 )
169 {
170 close();
171 throw TcpSocketException( TcpSocketException::cClosed, "TcpSocket closed.");
172 }
173 if( nRead < 0 )
174 {
175#ifdef WIN32
176 int iWSAError = bu_WSAGetLastError();
177 if( iWSAError == WSAEWOULDBLOCK )
178 return 0;
179#else
180 if( errno == ENETRESET || errno == ECONNRESET )
181 {
182 close();
183 throw TcpSocketException( TcpSocketException::cClosed,
184 strerror(errno) );
185 }
186 if( errno == EAGAIN )
187 return 0;
188 int iErr = errno;
189 close();
190 throw TcpSocketException( TcpSocketException::cRead, strerror(iErr) );
191#endif
192 }
193 return nRead;
194 }
195 return 0;
196}
197
198size_t Bu::TcpSocket::read( void *pBuf, size_t nBytes,
199 uint32_t nSec, uint32_t nUSec )
200{
201 struct timeval tv;
202 size_t nRead = 0;
203
204 fd_set rfds;
205 FD_ZERO(&rfds);
206 FD_SET(nTcpSocket, &rfds);
207
208#ifdef WIN32
209 DWORD dwStart = GetTickCount();
210 uint64_t uOver = dwStart + ((nUSec / 1000) * (nSec * 1000));
211 DWORD dwEnd = uOver>4294967295U?uOver-4294967295U:uOver;
212#else
213 struct timeval nt, ct;
214 gettimeofday( &nt, NULL );
215 nt.tv_sec += nSec;
216 nt.tv_usec += nUSec;
217#endif
218
219 for(;;)
220 {
221 tv.tv_sec = nSec;
222 tv.tv_usec = nUSec;
223 bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv );
224 nRead += read( ((char *)pBuf)+nRead, nBytes-nRead );
225 if( nRead >= nBytes )
226 break;
227#ifdef WIN32
228 DWORD dwNow = GetTickCount();
229 if( dwNow > dwEnd )
230 break;
231#else
232 gettimeofday( &ct, NULL );
233 if( (ct.tv_sec > nt.tv_sec) ||
234 (ct.tv_sec == nt.tv_sec &&
235 ct.tv_usec >= nt.tv_usec) )
236 break;
237#endif
238 }
239 return nRead;
240}
241
242size_t Bu::TcpSocket::write( const void *pBuf, size_t nBytes )
243{
244//#ifdef WIN32
245 int nWrote = TEMP_FAILURE_RETRY(
246 bu_send( nTcpSocket, (const char *) pBuf, nBytes, 0 ) );
247//#else
248// int nWrote = TEMP_FAILURE_RETRY( ::write( nTcpSocket, pBuf, nBytes ) );
249//#endif
250 if( nWrote < 0 )
251 {
252#ifdef WIN32
253 int iWSAError = bu_WSAGetLastError();
254 if( iWSAError == WSAEWOULDBLOCK )
255 return 0;
256#else
257 if( errno == EAGAIN ) return 0;
258#endif
259 throw TcpSocketException( TcpSocketException::cWrite, strerror(errno) );
260 }
261 return nWrote;
262}
263
264size_t Bu::TcpSocket::write( const void *pBuf, size_t nBytes, uint32_t nSec, uint32_t nUSec )
265{
266 struct timeval tv;
267 size_t nWrote = 0;
268
269 fd_set wfds;
270 FD_ZERO(&wfds);
271 FD_SET(nTcpSocket, &wfds);
272
273#ifdef WIN32
274 DWORD dwStart = GetTickCount();
275 uint64_t uOver = dwStart + ((nUSec / 1000) * (nSec * 1000));
276 DWORD dwEnd = uOver>4294967295U?uOver-4294967295U:uOver;
277#else
278 struct timeval nt, ct;
279 gettimeofday( &nt, NULL );
280 nt.tv_sec += nSec;
281 nt.tv_usec += nUSec;
282#endif
283
284 for(;;)
285 {
286 tv.tv_sec = nSec;
287 tv.tv_usec = nUSec;
288 bu_select( nTcpSocket+1, NULL, &wfds, NULL, &tv );
289 nWrote += write( ((char *)pBuf)+nWrote, nBytes-nWrote );
290 if( nWrote >= nBytes )
291 break;
292#ifdef WIN32
293 DWORD dwNow = GetTickCount();
294 if( dwNow > dwEnd )
295 break;
296#else
297 gettimeofday( &ct, NULL );
298 if( (ct.tv_sec > nt.tv_sec) ||
299 (ct.tv_sec == nt.tv_sec &&
300 ct.tv_usec >= nt.tv_usec) )
301 break;
302#endif
303 }
304 return nWrote;
305}
306
307long Bu::TcpSocket::tell()
308{
309 throw UnsupportedException();
310}
311
312void Bu::TcpSocket::seek( long )
313{
314 throw UnsupportedException();
315}
316
317void Bu::TcpSocket::setPos( long )
318{
319 throw UnsupportedException();
320}
321
322void Bu::TcpSocket::setPosEnd( long )
323{
324 throw UnsupportedException();
325}
326
327bool Bu::TcpSocket::isEos()
328{
329 return !bActive;
330}
331
332bool Bu::TcpSocket::canRead()
333{
334 fd_set rfds;
335 FD_ZERO(&rfds);
336 FD_SET(nTcpSocket, &rfds);
337 struct timeval tv = { 0, 0 };
338 int retval = bu_select( nTcpSocket+1, &rfds, NULL, NULL, &tv );
339 if( retval == -1 )
340 throw TcpSocketException(
341 TcpSocketException::cBadRead,
342 "Bad Read error"
343 );
344
345 if( !FD_ISSET( nTcpSocket, &rfds ) )
346 return false;
347 return true;
348}
349
350bool Bu::TcpSocket::canWrite()
351{
352 fd_set wfds;
353 FD_ZERO(&wfds);
354 FD_SET(nTcpSocket, &wfds);
355 struct timeval tv = { 0, 0 };
356 int retval = bu_select( nTcpSocket+1, NULL, &wfds, NULL, &tv );
357 if( retval == -1 )
358 throw TcpSocketException(
359 TcpSocketException::cBadRead,
360 "Bad Read error"
361 );
362 if( !FD_ISSET( nTcpSocket, &wfds ) )
363 return false;
364 return true;
365}
366
367bool Bu::TcpSocket::isReadable()
368{
369 return true;
370}
371
372bool Bu::TcpSocket::isWritable()
373{
374 return true;
375}
376
377bool Bu::TcpSocket::isSeekable()
378{
379 return false;
380}
381
382bool Bu::TcpSocket::isBlocking()
383{
384#ifndef WIN32
385 return ((fcntl( nTcpSocket, F_GETFL, 0 ) & O_NONBLOCK) != O_NONBLOCK);
386#else
387 return false;
388#endif
389}
390
391void Bu::TcpSocket::setBlocking( bool bBlocking )
392{
393 this->bBlocking = bBlocking;
394#ifndef WIN32
395 if( bBlocking )
396 {
397 fcntl( nTcpSocket, F_SETFL, fcntl( nTcpSocket, F_GETFL, 0 ) & (~O_NONBLOCK) );
398 }
399 else
400 {
401 fcntl( nTcpSocket, F_SETFL, fcntl( nTcpSocket, F_GETFL, 0 ) | O_NONBLOCK );
402 }
403#else
404 u_long iMode;
405 if( bBlocking )
406 iMode = 0;
407 else
408 iMode = 1;
409 //-------------------------
410 // Set the socket I/O mode: In this case FIONBIO
411 // enables or disables the blocking mode for the
412 // socket based on the numerical value of iMode.
413 // If iMode = 0, blocking is enabled;
414 // If iMode != 0, non-blocking mode is enabled.
415 bu_ioctlsocket(nTcpSocket, FIONBIO, &iMode);
416#endif
417}
418
419void Bu::TcpSocket::setSize( long )
420{
421}
422
423void Bu::TcpSocket::flush()
424{
425}
426
427bool Bu::TcpSocket::isOpen()
428{
429 return bActive;
430}
431
432void Bu::TcpSocket::setAddress()
433{
434 struct sockaddr_in addr;
435 socklen_t len = sizeof(addr);
436 addr.sin_family = AF_INET;
437 bu_getpeername( nTcpSocket, (sockaddr *)(&addr), &len );
438 sAddress = bu_inet_ntoa( addr.sin_addr );
439}
440
441Bu::FString Bu::TcpSocket::getAddress() const
442{
443 return sAddress;
444}
445
446Bu::TcpSocket::operator int() const
447{
448 return nTcpSocket;
449}
450