Rev 975 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed
| Rev 975 | Rev 976 | ||
|---|---|---|---|
| Line 18... | Line 18... | ||
| 18 | * Free Software Foundation, Inc., * |
18 | * Free Software Foundation, Inc., * |
| 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| 20 | ***************************************************************************/ |
20 | ***************************************************************************/ |
| 21 | 21 | ||
| 22 | #include "asyncsocket.h" |
22 | #include "asyncsocket.h" |
| 23 | #include <iostream> |
- | |
| - | 23 | ||
| 24 | Semaphore AsyncSocket::mySemaphore; |
24 | Semaphore AsyncSocket::mySemaphore; |
| 25 | 25 | ||
| 26 | AsyncSocket::AsyncSocket() |
26 | AsyncSocket::AsyncSocket() |
| 27 | { |
27 | { |
| 28 | mySocket = -1; |
28 | mySocket = -1; |
| 29 | myReconnectTimeout = |
29 | myReconnectTimeout = 0; |
| 30 | myForceReconnect = false; |
30 | myForceReconnect = false; |
| 31 | 31 | ||
| 32 | Thread<AsyncSocket>(); |
32 | Thread<AsyncSocket>(); |
| 33 | } |
33 | } |
| 34 | 34 | ||
| Line 36... | Line 36... | ||
| 36 | { |
36 | { |
| 37 | if (mySocket != -1) |
37 | if (mySocket != -1) |
| 38 | { |
38 | { |
| 39 | ::close(mySocket); |
39 | ::close(mySocket); |
| 40 | mySocket = -1; |
40 | mySocket = -1; |
| 41 | } |
41 | } |
| 42 | 42 | ||
| 43 | stop(); |
43 | stop(); |
| 44 | } |
44 | } |
| 45 | 45 | ||
| 46 | void AsyncSocket::run() |
46 | void AsyncSocket::run() |
| 47 | { |
47 | { |
| 48 | SyslogStream &slog = SyslogStream::getInstance(); |
48 | SyslogStream &slog = SyslogStream::getInstance(); |
| 49 | 49 | ||
| - | 50 | if (myReconnectTimeout == 0) |
|
| - | 51 | { |
|
| - | 52 | connect(); |
|
| - | 53 | } |
|
| - | 54 | else |
|
| - | 55 | { |
|
| 50 | reconnectLoop(); |
56 | reconnectLoop(); |
| - | 57 | } |
|
| 51 | 58 | ||
| 52 | char buf[MAXBUFFER + 1]; |
59 | char buf[MAXBUFFER + 1]; |
| 53 | string data; |
60 | string data; |
| 54 | int status; |
61 | int status; |
| 55 | int rc; |
62 | int rc; |
| 56 | int timeSince = time(NULL) + 10; |
63 | int timeSince = time(NULL) + 10; |
| 57 | 64 | ||
| 58 | AsyncSocket::mySemaphore.lock(); |
- | |
| - | 65 | ||
| 59 | 66 | ||
| 60 | try |
67 | try |
| 61 | { |
68 | { |
| 62 | while (1) |
69 | while (1) |
| 63 | { |
70 | { |
| - | 71 | AsyncSocket::mySemaphore.lock(); |
|
| - | 72 | ||
| - | 73 | if (myReconnectTimeout == 0) |
|
| - | 74 | { |
|
| 64 |
|
75 | rc = mySemaphore.wait(); |
| - | 76 | } |
|
| - | 77 | else |
|
| - | 78 | { |
|
| - | 79 | rc = mySemaphore.wait(10); |
|
| - | 80 | } |
|
| - | 81 | ||
| - | 82 | AsyncSocket::mySemaphore.unlock(); |
|
| 65 | 83 | ||
| 66 | if (myForceReconnect) |
84 | if (myForceReconnect) |
| 67 | { |
85 | { |
| 68 | slog << "Disconnected from server.\n"; |
86 | slog << "Disconnected from server.\n"; |
| 69 | reconnectLoop(); |
87 | reconnectLoop(); |
| 70 | timeSince = time(NULL) + 10; |
88 | timeSince = time(NULL) + 10; |
| 71 | continue; |
89 | continue; |
| 72 | } |
90 | } |
| 73 | 91 | ||
| Line 144... | Line 162... | ||
| 144 | throw new SocketException("Some bit in the flags argument is inappropriate for the socket type."); |
162 | throw new SocketException("Some bit in the flags argument is inappropriate for the socket type."); |
| 145 | 163 | ||
| 146 | case EPIPE: |
164 | case EPIPE: |
| 147 | throw new SocketException("The local end has been shut down on a connection oriented socket. In this case the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set."); |
165 | throw new SocketException("The local end has been shut down on a connection oriented socket. In this case the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set."); |
| 148 | 166 | ||
| 149 | default: |
167 | default: |
| 150 | slog << "Unknow exception: " |
168 | slog << "Unknow exception: " + itos(errno) + "\n"; |
| 151 | break; |
169 | break; |
| 152 | } |
170 | } |
| 153 | } |
171 | } |
| 154 | } |
172 | } |
| 155 | 173 | ||
| 156 | memset(buf, 0, MAXBUFFER + 1); |
174 | memset(buf, 0, MAXBUFFER + 1); |
| 157 | 175 | ||
| 158 | status = ::recv(mySocket, buf, MAXBUFFER, 0); |
176 | status = ::recv(mySocket, buf, MAXBUFFER, 0); |
| 159 | - | ||
| 160 | //slog << "Receiving: " << buf << "\n"; |
- | |
| 161 | 177 | ||
| 162 | if (status == -1) |
178 | if (status == -1) |
| 163 | { |
179 | { |
| 164 | switch (errno) |
180 | switch (errno) |
| 165 | { |
181 | { |
| Line 167... | Line 183... | ||
| 167 | //slog << "The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received.\n"; |
183 | //slog << "The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received.\n"; |
| 168 | break; |
184 | break; |
| 169 | 185 | ||
| 170 | case EBADF: |
186 | case EBADF: |
| 171 | throw new SocketException("The argument s is an invalid descriptor."); |
187 | throw new SocketException("The argument s is an invalid descriptor."); |
| 172 | 188 | ||
| 173 | case ECONNREFUSED: |
189 | case ECONNREFUSED: |
| 174 | throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service)."); |
190 | throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service)."); |
| 175 | 191 | ||
| 176 | case EFAULT: |
192 | case EFAULT: |
| 177 | throw new SocketException("The receive buffer pointer(s) point outside the process's address space."); |
193 | throw new SocketException("The receive buffer pointer(s) point outside the process's address space."); |
| Line 181... | Line 197... | ||
| 181 | 197 | ||
| 182 | case EINVAL: |
198 | case EINVAL: |
| 183 | //throw new SocketException("2Invalid argument passed."); |
199 | //throw new SocketException("2Invalid argument passed."); |
| 184 | slog << "Disconnected from server.\n"; |
200 | slog << "Disconnected from server.\n"; |
| 185 | reconnectLoop(); |
201 | reconnectLoop(); |
| - | 202 | timeSince = time(NULL) + 10; |
|
| 186 | break; |
203 | break; |
| 187 | 204 | ||
| 188 | case ENOMEM: |
205 | case ENOMEM: |
| 189 | throw new SocketException("Could not allocate memory for recvmsg()."); |
206 | throw new SocketException("Could not allocate memory for recvmsg()."); |
| 190 | 207 | ||
| 191 | case ENOTCONN: |
208 | case ENOTCONN: |
| 192 | throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2))."); |
209 | throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2))."); |
| 193 | 210 | ||
| 194 | case ENOTSOCK: |
211 | case ENOTSOCK: |
| 195 | throw new SocketException("The argument s does not refer to a socket."); |
212 | throw new SocketException("The argument s does not refer to a socket."); |
| 196 | 213 | ||
| 197 | default: |
214 | default: |
| 198 | slog << "Unknow exception: " |
215 | slog << "Unknow exception: " + itos(errno) + "\n"; |
| 199 | break; |
216 | break; |
| 200 | } |
217 | } |
| 201 | } |
218 | } |
| 202 | else if (status == 0) |
219 | else if (status == 0) |
| 203 | { |
220 | { |
| 204 | slog << "Disconnected from server.\n"; |
221 | slog << "Disconnected from server.\n"; |
| 205 | reconnectLoop(); |
222 | reconnectLoop(); |
| - | 223 | timeSince = time(NULL); |
|
| 206 | } |
224 | } |
| 207 | else if (status > 0) |
225 | else if (status > 0) |
| 208 | { |
226 | { |
| - | 227 | timeSince = time(NULL) + 10; |
|
| - | 228 | ||
| 209 | data = buf; |
229 | data = buf; |
| - | 230 | ||
| - | 231 | //slog << "Receiving: " + data + "\n"; |
|
| 210 | 232 | ||
| 211 | myInQueue.push(data); |
233 | myInQueue.push(data); |
| 212 | 234 | ||
| 213 | setEvent(ASYNCSOCKET_EVENT_DATA); |
235 | setEvent(ASYNCSOCKET_EVENT_DATA); |
| 214 | - | ||
| 215 | timeSince = time(NULL); |
- | |
| 216 | } |
236 | } |
| 217 | 237 | ||
| 218 | if (timeSince + 10 < time(NULL)) |
238 | if (timeSince + 10 < time(NULL)) |
| 219 | { |
239 | { |
| 220 | setEvent(ASYNCSOCKET_EVENT_INACTIVITY); |
240 | setEvent(ASYNCSOCKET_EVENT_INACTIVITY); |
| 221 | timeSince = time(NULL); |
241 | timeSince = time(NULL); |
| 222 | } |
242 | } |
| 223 | } |
243 | } |
| 224 | } |
244 | } |
| 225 | } |
245 | } |
| 226 | catch (SocketException *e) |
246 | catch (SocketException *e) |
| 227 | { |
247 | { |
| 228 | slog << "Exception: " |
248 | slog << "Exception: " + e->getDescription() + "\n"; |
| 229 | mySemaphore. |
249 | //mySemaphore.unlock(); |
| 230 | setEvent(ASYNCSOCKET_EVENT_DIED); |
250 | setEvent(ASYNCSOCKET_EVENT_DIED); |
| 231 | stop(); |
251 | stop(); |
| 232 | } |
252 | } |
| 233 | 253 | ||
| 234 | close(); |
254 | close(); |
| 235 | 255 | ||
| 236 | mySemaphore. |
256 | //mySemaphore.unlock(); |
| 237 | } |
257 | } |
| 238 | 258 | ||
| 239 | void AsyncSocket::reconnectLoop() |
259 | void AsyncSocket::reconnectLoop() |
| 240 | { |
260 | { |
| - | 261 | if (myReconnectTimeout == 0) |
|
| - | 262 | { |
|
| - | 263 | throw new SocketException("Connection is closed."); |
|
| - | 264 | } |
|
| - | 265 | ||
| 241 | SyslogStream &slog = SyslogStream::getInstance(); |
266 | SyslogStream &slog = SyslogStream::getInstance(); |
| 242 | 267 | ||
| 243 | while (1) |
268 | while (1) |
| 244 | { |
269 | { |
| 245 | try |
270 | try |
| 246 | { |
271 | { |
| 247 | slog << "Trying to connect...\n"; |
- | |
| 248 | connect(); |
272 | connect(); |
| 249 | slog << "Connection established.\n"; |
- | |
| 250 | break; |
273 | break; |
| 251 | } |
274 | } |
| 252 | catch (SocketException *e) |
275 | catch (SocketException *e) |
| 253 | { |
276 | { |
| 254 | slog << "Could not connect: " |
277 | slog << "Could not connect: " + e->getDescription() + "\n"; |
| 255 | slog << "Will try again in " |
278 | slog << "Will try again in " + itos(myReconnectTimeout) + " seconds\n"; |
| 256 | sleep(myReconnectTimeout); |
279 | sleep(myReconnectTimeout); |
| 257 | } |
280 | } |
| 258 | } |
281 | } |
| 259 | } |
282 | } |
| 260 | 283 | ||
| 261 | void AsyncSocket::connect() |
284 | void AsyncSocket::connect() |
| 262 | { |
285 | { |
| - | 286 | SyslogStream &slog = SyslogStream::getInstance(); |
|
| - | 287 | ||
| 263 | close(); |
288 | close(); |
| - | 289 | ||
| - | 290 | slog << "Trying to connect...\n"; |
|
| 264 | 291 | ||
| 265 | mySocket = ::socket(AF_INET, SOCK_STREAM, 0); |
292 | mySocket = ::socket(AF_INET, SOCK_STREAM, 0); |
| 266 | 293 | ||
| 267 | int on = 1; |
294 | int on = 1; |
| 268 | int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); |
295 | int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); |
| 269 | if (status == -1) |
296 | if (status == -1) |
| 270 | { |
297 | { |
| 271 | throw new SocketException("Connect:Reuseaddress: " + itos(errno)); |
298 | throw new SocketException("Connect:Reuseaddress: " + itos(errno)); |
| 272 | } |
299 | } |
| 273 | 300 | ||
| 274 | ///FIXME: Verify that this works |
301 | ///FIXME: Verify that this works |
| 275 | status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on)); |
302 | status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on)); |
| 276 | if (status == -1) |
303 | if (status == -1) |
| 277 | { |
304 | { |
| 278 | throw new SocketException("Connect:Keepalive: " + itos(errno)); |
305 | throw new SocketException("Connect:Keepalive: " + itos(errno)); |
| 279 | } |
306 | } |
| 280 | 307 | ||
| 281 | ///FIXME: Verify that this works |
308 | ///FIXME: Verify that this works |
| 282 | status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on)); |
309 | status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on)); |
| 283 | if (status == -1) |
310 | if (status == -1) |
| 284 | { |
311 | { |
| 285 | throw new SocketException("Connect:Keepidle: " + itos(errno)); |
312 | throw new SocketException("Connect:Keepidle: " + itos(errno)); |
| 286 | } |
313 | } |
| 287 | 314 | ||
| 288 | memset(&myAddressStruct, 0, sizeof(myAddressStruct)); |
315 | memset(&myAddressStruct, 0, sizeof(myAddressStruct)); |
| 289 | 316 | ||
| 290 | myAddressStruct.sin_family = AF_INET; |
317 | myAddressStruct.sin_family = AF_INET; |
| 291 | myAddressStruct.sin_port = htons(myPort); |
318 | myAddressStruct.sin_port = htons(myPort); |
| 292 | 319 | ||
| - | 320 | struct hostent *hptr = gethostbyname(myAddress.c_str()); |
|
| - | 321 | if (hptr == NULL) |
|
| - | 322 | { |
|
| - | 323 | throw new SocketException("Connect: Could not resolv ip address"); |
|
| - | 324 | } |
|
| - | 325 | ||
| - | 326 | memcpy(&myAddressStruct.sin_addr, hptr->h_addr, hptr->h_length); |
|
| - | 327 | /* |
|
| 293 | status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr); |
328 | status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr); |
| 294 | 329 | ||
| 295 | if (status == -1) |
330 | if (status == -1) |
| 296 | { |
331 | { |
| 297 | if (errno == EAFNOSUPPORT) |
332 | if (errno == EAFNOSUPPORT) |
| 298 | throw new SocketException("Connect: EAFNOSUPPORT"); |
333 | throw new SocketException("Connect: EAFNOSUPPORT"); |
| 299 | } |
334 | } |
| - | 335 | */ |
|
| - | 336 | ||
| 300 | 337 | ||
| 301 | status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct)); |
338 | status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct)); |
| 302 | 339 | ||
| 303 | if (status == -1) |
340 | if (status == -1) |
| 304 | { |
341 | { |
| 305 | switch (errno) |
342 | switch (errno) |
| 306 | { |
343 | { |
| 307 | case EACCES: |
344 | case EACCES: |
| 308 | throw new SocketException("For Unix domain sockets, which are identified by pathname: Write permission is denied on the socket file, or search per- mission is denied for one of the directories in the path prefix. (See also path_resolution(7).)"); |
345 | throw new SocketException("For Unix domain sockets, which are identified by pathname: Write permission is denied on the socket file, or search per- mission is denied for one of the directories in the path prefix. (See also path_resolution(7).)"); |
| 309 | 346 | ||
| 310 | case EPERM: |
347 | case EPERM: |
| 311 | throw new SocketException("The user tried to connect to a broadcast address without having the socket broadcast flag enabled or the connection request failed because of a local firewall rule."); |
348 | throw new SocketException("The user tried to connect to a broadcast address without having the socket broadcast flag enabled or the connection request failed because of a local firewall rule."); |
| 312 | 349 | ||
| 313 | case EADDRINUSE: |
350 | case EADDRINUSE: |
| 314 | throw new SocketException("Local address is already in use."); |
351 | throw new SocketException("Local address is already in use."); |
| 315 | 352 | ||
| 316 | case EAFNOSUPPORT: |
353 | case EAFNOSUPPORT: |
| 317 | throw new SocketException("The passed address didn't have the correct address family in its sa_family field."); |
354 | throw new SocketException("The passed address didn't have the correct address family in its sa_family field."); |
| 318 | 355 | ||
| 319 | case EAGAIN: |
356 | case EAGAIN: |
| 320 | throw new SocketException("No more free local ports or insufficient entries in the routing cache. For AF_INET see the net.ipv4.ip_local_port_range sysctl in ip(7) on how to increase the number of local ports."); |
357 | throw new SocketException("No more free local ports or insufficient entries in the routing cache. For AF_INET see the net.ipv4.ip_local_port_range sysctl in ip(7) on how to increase the number of local ports."); |
| Line 325... | Line 362... | ||
| 325 | case EBADF: |
362 | case EBADF: |
| 326 | throw new SocketException("The file descriptor is not a valid index in the descriptor table."); |
363 | throw new SocketException("The file descriptor is not a valid index in the descriptor table."); |
| 327 | 364 | ||
| 328 | case ECONNREFUSED: |
365 | case ECONNREFUSED: |
| 329 | throw new SocketException("No-one listening on the remote address."); |
366 | throw new SocketException("No-one listening on the remote address."); |
| 330 | 367 | ||
| 331 | case EFAULT: |
368 | case EFAULT: |
| 332 | throw new SocketException("The socket structure address is outside the user's address space."); |
369 | throw new SocketException("The socket structure address is outside the user's address space."); |
| 333 | 370 | ||
| 334 | case EINPROGRESS: |
371 | case EINPROGRESS: |
| 335 | throw new SocketException("The socket is non-blocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure)."); |
372 | throw new SocketException("The socket is non-blocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure)."); |
| 336 | 373 | ||
| 337 | case EINTR: |
374 | case EINTR: |
| 338 | throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7)."); |
375 | throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7)."); |
| 339 | 376 | ||
| 340 | case EISCONN: |
377 | case EISCONN: |
| 341 | throw new SocketException("The socket is already connected."); |
378 | throw new SocketException("The socket is already connected."); |
| Line 346... | Line 383... | ||
| 346 | case ENOTSOCK: |
383 | case ENOTSOCK: |
| 347 | throw new SocketException("The file descriptor is not associated with a socket."); |
384 | throw new SocketException("The file descriptor is not associated with a socket."); |
| 348 | 385 | ||
| 349 | case ETIMEDOUT: |
386 | case ETIMEDOUT: |
| 350 | throw new SocketException("Timeout while attempting connection. The server may be too busy to accept new connections. Note that for IP sockets the timeout may be very long when syncookies are enabled on the server."); |
387 | throw new SocketException("Timeout while attempting connection. The server may be too busy to accept new connections. Note that for IP sockets the timeout may be very long when syncookies are enabled on the server."); |
| 351 | 388 | ||
| 352 | default: |
389 | default: |
| 353 | throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno)); |
390 | throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno)); |
| 354 | } |
391 | } |
| 355 | } |
392 | } |
| 356 | 393 | ||
| Line 368... | Line 405... | ||
| 368 | throw new SocketException("Async socket fcntl failed"); |
405 | throw new SocketException("Async socket fcntl failed"); |
| 369 | 406 | ||
| 370 | fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC); |
407 | fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC); |
| 371 | 408 | ||
| 372 | myForceReconnect = false; |
409 | myForceReconnect = false; |
| - | 410 | ||
| - | 411 | slog << "Connection established.\n"; |
|
| 373 | } |
412 | } |
| 374 | 413 | ||
| 375 | void AsyncSocket::close() |
414 | void AsyncSocket::close() |
| 376 | { |
415 | { |
| 377 | if (mySocket != -1) |
416 | if (mySocket != -1) |