diff options
author | Mike Buland <eichlan@xagasoft.com> | 2010-10-16 03:02:11 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2010-10-16 03:02:11 +0000 |
commit | 9031e2af7dd4e65ec70890ee78a7cf600d1b2cc5 (patch) | |
tree | 17bc9d96b13d16d79385016c087321fc1267743f /src/tcpsocket.cpp | |
parent | 93c028162318a00b9bd03fc4a48383f830cc529d (diff) | |
download | libbu++-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.cpp | 450 |
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 | |||
32 | namespace Bu { subExceptionDef( TcpSocketException ) } | ||
33 | |||
34 | Bu::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 | |||
45 | Bu::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 | |||
129 | Bu::TcpSocket::~TcpSocket() | ||
130 | { | ||
131 | close(); | ||
132 | } | ||
133 | |||
134 | void 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 | |||
152 | size_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 | |||
198 | size_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 | |||
242 | size_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 | |||
264 | size_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 | |||
307 | long Bu::TcpSocket::tell() | ||
308 | { | ||
309 | throw UnsupportedException(); | ||
310 | } | ||
311 | |||
312 | void Bu::TcpSocket::seek( long ) | ||
313 | { | ||
314 | throw UnsupportedException(); | ||
315 | } | ||
316 | |||
317 | void Bu::TcpSocket::setPos( long ) | ||
318 | { | ||
319 | throw UnsupportedException(); | ||
320 | } | ||
321 | |||
322 | void Bu::TcpSocket::setPosEnd( long ) | ||
323 | { | ||
324 | throw UnsupportedException(); | ||
325 | } | ||
326 | |||
327 | bool Bu::TcpSocket::isEos() | ||
328 | { | ||
329 | return !bActive; | ||
330 | } | ||
331 | |||
332 | bool 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 | |||
350 | bool 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 | |||
367 | bool Bu::TcpSocket::isReadable() | ||
368 | { | ||
369 | return true; | ||
370 | } | ||
371 | |||
372 | bool Bu::TcpSocket::isWritable() | ||
373 | { | ||
374 | return true; | ||
375 | } | ||
376 | |||
377 | bool Bu::TcpSocket::isSeekable() | ||
378 | { | ||
379 | return false; | ||
380 | } | ||
381 | |||
382 | bool 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 | |||
391 | void 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 | |||
419 | void Bu::TcpSocket::setSize( long ) | ||
420 | { | ||
421 | } | ||
422 | |||
423 | void Bu::TcpSocket::flush() | ||
424 | { | ||
425 | } | ||
426 | |||
427 | bool Bu::TcpSocket::isOpen() | ||
428 | { | ||
429 | return bActive; | ||
430 | } | ||
431 | |||
432 | void 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 | |||
441 | Bu::FString Bu::TcpSocket::getAddress() const | ||
442 | { | ||
443 | return sAddress; | ||
444 | } | ||
445 | |||
446 | Bu::TcpSocket::operator int() const | ||
447 | { | ||
448 | return nTcpSocket; | ||
449 | } | ||
450 | |||