summaryrefslogtreecommitdiff
path: root/src/tcpsocket.cpp
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2010-10-16 03:02:11 +0000
committerMike Buland <eichlan@xagasoft.com>2010-10-16 03:02:11 +0000
commit9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5 (patch)
tree17bc9d96b13d16d79385016c087321fc1267743f /src/tcpsocket.cpp
parent93c028162318a00b9bd03fc4a48383f830cc529d (diff)
downloadlibbu++-9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5.tar.gz
libbu++-9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5.tar.bz2
libbu++-9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5.tar.xz
libbu++-9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5.zip
Many, many changes. Documentation changes, renamed the socket class to
TcpSocket, fixed many other things, and finally removed ParamProc. Anything that needs it will now have to switch to OptParser.
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