diff options
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 | |||