Rev 969 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed
| Rev 969 | Rev 974 | ||
|---|---|---|---|
| Line 25... | Line 25... | ||
| 25 | 25 | ||
| 26 | AsyncSocket::AsyncSocket() |
26 | AsyncSocket::AsyncSocket() |
| 27 | { |
27 | { |
| 28 | mySocket = -1; |
28 | mySocket = -1; |
| 29 | myReconnectTimeout = 10; |
29 | myReconnectTimeout = 10; |
| - | 30 | myForceReconnect = false; |
|
| 30 | 31 | ||
| 31 | Thread<AsyncSocket>(); |
32 | Thread<AsyncSocket>(); |
| 32 | } |
33 | } |
| 33 | 34 | ||
| 34 | AsyncSocket::~AsyncSocket() |
35 | AsyncSocket::~AsyncSocket() |
| Line 49... | Line 50... | ||
| 49 | reconnectLoop(); |
50 | reconnectLoop(); |
| 50 | 51 | ||
| 51 | char buf[MAXBUFFER + 1]; |
52 | char buf[MAXBUFFER + 1]; |
| 52 | string data; |
53 | string data; |
| 53 | int status; |
54 | int status; |
| - | 55 | int rc; |
|
| - | 56 | int timeSince = time(NULL) + 10; |
|
| 54 | 57 | ||
| 55 | AsyncSocket::mySemaphore.lock(); |
58 | AsyncSocket::mySemaphore.lock(); |
| 56 | 59 | ||
| 57 | try |
60 | try |
| 58 | { |
61 | { |
| 59 | while (1) |
62 | while (1) |
| 60 | { |
63 | { |
| 61 | mySemaphore.wait |
64 | rc = mySemaphore.wait(5); |
| - | 65 | ||
| - | 66 | if (myForceReconnect) |
|
| - | 67 | { |
|
| - | 68 | slog << "Disconnected from server.\n"; |
|
| - | 69 | reconnectLoop(); |
|
| - | 70 | timeSince = time(NULL) + 10; |
|
| - | 71 | continue; |
|
| - | 72 | } |
|
| 62 | 73 | ||
| 63 | //cout << "Socket awoken...\n"; |
74 | //cout << "Socket awoken...\n"; |
| 64 | 75 | ||
| - | 76 | if (rc == ETIMEDOUT) |
|
| - | 77 | { |
|
| - | 78 | /* Socket timed out, this means we have not received anything in some time |
|
| - | 79 | and we should check the connection */ |
|
| - | 80 | ||
| - | 81 | setEvent(ASYNCSOCKET_EVENT_INACTIVITY); |
|
| - | 82 | timeSince = time(NULL); |
|
| - | 83 | } |
|
| - | 84 | else |
|
| - | 85 | { |
|
| 65 | while (myOutQueue.size() > 0) |
86 | while (myOutQueue.size() > 0) |
| 66 | { |
87 | { |
| 67 | data = myOutQueue.pop(); |
88 | data = myOutQueue.pop(); |
| 68 | 89 | ||
| 69 | //slog << "Sending: " << data << "\n"; |
90 | //slog << "Sending: " << data << "\n"; |
| 70 | 91 | ||
| 71 | status = ::send(mySocket, data.c_str(), data.size(), 0); |
92 | status = ::send(mySocket, data.c_str(), data.size(), 0); |
| - | 93 | ||
| - | 94 | //slog << "Status: " << status << "\n"; |
|
| 72 | 95 | ||
| 73 | if (status == -1) |
96 | if (status == -1) |
| 74 | { |
97 | { |
| 75 | switch (errno) |
98 | switch (errno) |
| 76 | { |
99 | { |
| Line 78... | Line 101... | ||
| 78 | throw new SocketException("(For Unix domain sockets, which are identified by pathname) Write permission is denied on the destination socket file, or search permission is denied for one of the directories the path prefix. (See path_resolution(7).)"); |
101 | throw new SocketException("(For Unix domain sockets, which are identified by pathname) Write permission is denied on the destination socket file, or search permission is denied for one of the directories the path prefix. (See path_resolution(7).)"); |
| 79 | 102 | ||
| 80 | case EAGAIN: |
103 | case EAGAIN: |
| 81 | //slog << "The socket is marked non-blocking and the requested operation would block.\n"; |
104 | //slog << "The socket is marked non-blocking and the requested operation would block.\n"; |
| 82 | break; |
105 | break; |
| 83 | 106 | ||
| 84 | case EBADF: |
107 | case EBADF: |
| 85 | throw new SocketException("An invalid descriptor was specified."); |
108 | throw new SocketException("An invalid descriptor was specified."); |
| 86 | 109 | ||
| 87 | case ECONNRESET: |
110 | case ECONNRESET: |
| 88 | throw new SocketException("Connection reset by peer."); |
111 | throw new SocketException("Connection reset by peer."); |
| Line 93... | Line 116... | ||
| 93 | case EFAULT: |
116 | case EFAULT: |
| 94 | throw new SocketException("An invalid user space address was specified for an argument."); |
117 | throw new SocketException("An invalid user space address was specified for an argument."); |
| 95 | 118 | ||
| 96 | case EINTR: |
119 | case EINTR: |
| 97 | throw new SocketException("A signal occurred before any data was transmitted; see signal(7)."); |
120 | throw new SocketException("A signal occurred before any data was transmitted; see signal(7)."); |
| 98 | 121 | ||
| 99 | case EINVAL: |
122 | case EINVAL: |
| 100 | throw new SocketException(" |
123 | throw new SocketException("1Invalid argument passed."); |
| 101 | 124 | ||
| 102 | case EISCONN: |
125 | case EISCONN: |
| 103 | throw new SocketException("The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.)"); |
126 | throw new SocketException("The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.)"); |
| 104 | 127 | ||
| 105 | case EMSGSIZE: |
128 | case EMSGSIZE: |
| 106 | throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible."); |
129 | throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible."); |
| 107 | 130 | ||
| 108 | case ENOBUFS: |
131 | case ENOBUFS: |
| 109 | throw new SocketException("The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)"); |
132 | throw new SocketException("The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)"); |
| 110 | 133 | ||
| 111 | case ENOMEM: |
134 | case ENOMEM: |
| 112 | throw new SocketException("No memory available."); |
135 | throw new SocketException("No memory available."); |
| 113 | 136 | ||
| 114 | case ENOTCONN: |
137 | case ENOTCONN: |
| 115 | throw new SocketException("The socket is not connected, and no target has been given."); |
138 | throw new SocketException("The socket is not connected, and no target has been given."); |
| 116 | 139 | ||
| 117 | case ENOTSOCK: |
140 | case ENOTSOCK: |
| 118 | throw new SocketException("The argument s is not a socket."); |
141 | throw new SocketException("The argument s is not a socket."); |
| Line 123... | Line 146... | ||
| 123 | case EPIPE: |
146 | case EPIPE: |
| 124 | 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."); |
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."); |
| 125 | 148 | ||
| 126 | default: |
149 | default: |
| 127 | slog << "Unknow exception: " << errno << "\n"; |
150 | slog << "Unknow exception: " << errno << "\n"; |
| 128 | break; |
151 | break; |
| 129 | } |
152 | } |
| 130 | } |
153 | } |
| 131 | } |
154 | } |
| 132 | 155 | ||
| 133 | memset(buf, 0, MAXBUFFER + 1); |
156 | memset(buf, 0, MAXBUFFER + 1); |
| 134 | 157 | ||
| 135 | status = ::recv(mySocket, buf, MAXBUFFER, 0); |
158 | status = ::recv(mySocket, buf, MAXBUFFER, 0); |
| - | 159 | ||
| - | 160 | //slog << "Receiving: " << buf << "\n"; |
|
| 136 | 161 | ||
| 137 | if (status == -1) |
162 | if (status == -1) |
| 138 | { |
163 | { |
| 139 | switch (errno) |
164 | switch (errno) |
| 140 | { |
165 | { |
| 141 | case EAGAIN: |
166 | case EAGAIN: |
| 142 | //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"; |
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"; |
| 143 | break; |
168 | break; |
| 144 | 169 | ||
| 145 | case EBADF: |
170 | case EBADF: |
| 146 | throw new SocketException("The argument s is an invalid descriptor."); |
171 | throw new SocketException("The argument s is an invalid descriptor."); |
| 147 | 172 | ||
| 148 | case ECONNREFUSED: |
173 | case ECONNREFUSED: |
| 149 | throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service)."); |
174 | throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service)."); |
| 150 | 175 | ||
| 151 | case EFAULT: |
176 | case EFAULT: |
| 152 | throw new SocketException("The receive buffer pointer(s) point outside the process's address space."); |
177 | throw new SocketException("The receive buffer pointer(s) point outside the process's address space."); |
| 153 | 178 | ||
| 154 | case EINTR: |
179 | case EINTR: |
| 155 | throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7)."); |
180 | throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7)."); |
| 156 | 181 | ||
| 157 | case EINVAL: |
182 | case EINVAL: |
| 158 |
|
183 | //throw new SocketException("2Invalid argument passed."); |
| - | 184 | slog << "Disconnected from server.\n"; |
|
| - | 185 | reconnectLoop(); |
|
| - | 186 | break; |
|
| 159 | 187 | ||
| 160 | case ENOMEM: |
188 | case ENOMEM: |
| 161 | throw new SocketException("Could not allocate memory for recvmsg()."); |
189 | throw new SocketException("Could not allocate memory for recvmsg()."); |
| 162 | 190 | ||
| 163 | case ENOTCONN: |
191 | case ENOTCONN: |
| 164 | throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2))."); |
192 | throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2))."); |
| 165 | 193 | ||
| 166 | case ENOTSOCK: |
194 | case ENOTSOCK: |
| 167 | throw new SocketException("The argument s does not refer to a socket."); |
195 | throw new SocketException("The argument s does not refer to a socket."); |
| 168 | 196 | ||
| 169 | default: |
197 | default: |
| 170 | slog << "Unknow exception: " << errno << "\n"; |
198 | slog << "Unknow exception: " << errno << "\n"; |
| 171 | break; |
199 | break; |
| 172 | } |
200 | } |
| 173 | } |
201 | } |
| 174 | else if (status == 0) |
202 | else if (status == 0) |
| 175 | { |
203 | { |
| 176 | slog << "Disconnected from server.\n"; |
204 | slog << "Disconnected from server.\n"; |
| 177 | reconnectLoop(); |
205 | reconnectLoop(); |
| 178 | } |
206 | } |
| 179 | else if (status > 0) |
207 | else if (status > 0) |
| 180 | { |
208 | { |
| 181 | data = buf; |
209 | data = buf; |
| 182 | 210 | ||
| 183 | myInQueue.push(data); |
211 | myInQueue.push(data); |
| 184 | 212 | ||
| 185 | setEvent(ASYNCSOCKET_EVENT_DATA); |
213 | setEvent(ASYNCSOCKET_EVENT_DATA); |
| - | 214 | ||
| - | 215 | timeSince = time(NULL); |
|
| - | 216 | } |
|
| - | 217 | ||
| - | 218 | if (timeSince + 10 < time(NULL)) |
|
| - | 219 | { |
|
| - | 220 | setEvent(ASYNCSOCKET_EVENT_INACTIVITY); |
|
| - | 221 | timeSince = time(NULL); |
|
| - | 222 | } |
|
| 186 | } |
223 | } |
| 187 | } |
224 | } |
| 188 | } |
225 | } |
| 189 | catch (SocketException *e) |
226 | catch (SocketException *e) |
| 190 | { |
227 | { |
| 191 | slog << "Exception: " << e->getDescription() << "\n"; |
228 | slog << "Exception: " << e->getDescription() << "\n"; |
| 192 | mySemaphore.unlock(); |
229 | mySemaphore.unlock(); |
| 193 | setEvent(ASYNCSOCKET_EVENT_DIED); |
230 | setEvent(ASYNCSOCKET_EVENT_DIED); |
| 194 | stop(); |
231 | stop(); |
| 195 | } |
232 | } |
| 196 | 233 | ||
| 197 | close(); |
234 | close(); |
| 198 | 235 | ||
| 199 | mySemaphore.unlock(); |
236 | mySemaphore.unlock(); |
| 200 | } |
237 | } |
| 201 | 238 | ||
| 202 | void AsyncSocket::reconnectLoop() |
239 | void AsyncSocket::reconnectLoop() |
| 203 | { |
240 | { |
| 204 | SyslogStream &slog = SyslogStream::getInstance(); |
241 | SyslogStream &slog = SyslogStream::getInstance(); |
| 205 | 242 | ||
| 206 | while (1) |
243 | while (1) |
| 207 | { |
244 | { |
| 208 | try |
245 | try |
| 209 | { |
246 | { |
| 210 | slog << "Trying to connect...\n"; |
247 | slog << "Trying to connect...\n"; |
| 211 | connect(); |
248 | connect(); |
| 212 | slog << "Connection established.\n"; |
249 | slog << "Connection established.\n"; |
| Line 220... | Line 257... | ||
| 220 | } |
257 | } |
| 221 | } |
258 | } |
| 222 | } |
259 | } |
| 223 | 260 | ||
| 224 | void AsyncSocket::connect() |
261 | void AsyncSocket::connect() |
| 225 | { |
262 | { |
| 226 | if (mySocket != -1) |
263 | if (mySocket != -1) |
| 227 | { |
264 | { |
| 228 | ::close(mySocket); |
265 | ::close(mySocket); |
| 229 | mySocket = -1; |
266 | mySocket = -1; |
| 230 | } |
267 | } |
| 231 | 268 | ||
| 232 | mySocket = ::socket(AF_INET, SOCK_STREAM, 0); |
269 | mySocket = ::socket(AF_INET, SOCK_STREAM, 0); |
| 233 | 270 | ||
| 234 | // TIME_WAIT - argh |
- | |
| 235 | int on = 1; |
271 | int on = 1; |
| 236 | int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); |
272 | int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); |
| 237 | if (status == -1) |
273 | if (status == -1) |
| 238 | { |
274 | { |
| - | 275 | throw new SocketException("Connect:Reuseaddress: " + itos(errno)); |
|
| - | 276 | } |
|
| - | 277 | ||
| - | 278 | ///FIXME: Verify that this works |
|
| - | 279 | status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on)); |
|
| - | 280 | if (status == -1) |
|
| - | 281 | { |
|
| - | 282 | throw new SocketException("Connect:Keepalive: " + itos(errno)); |
|
| - | 283 | } |
|
| - | 284 | ||
| - | 285 | ///FIXME: Verify that this works |
|
| - | 286 | status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on)); |
|
| - | 287 | if (status == -1) |
|
| - | 288 | { |
|
| 239 | throw new SocketException("Connect: " + itos(errno)); |
289 | throw new SocketException("Connect:Keepidle: " + itos(errno)); |
| 240 | } |
290 | } |
| 241 | 291 | ||
| 242 | memset(&myAddressStruct, 0, sizeof(myAddressStruct)); |
292 | memset(&myAddressStruct, 0, sizeof(myAddressStruct)); |
| 243 | 293 | ||
| 244 | myAddressStruct.sin_family = AF_INET; |
294 | myAddressStruct.sin_family = AF_INET; |
| Line 320... | Line 370... | ||
| 320 | 370 | ||
| 321 | if (flags < 0) |
371 | if (flags < 0) |
| 322 | throw new SocketException("Async socket fcntl failed"); |
372 | throw new SocketException("Async socket fcntl failed"); |
| 323 | 373 | ||
| 324 | fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC); |
374 | fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC); |
| - | 375 | ||
| - | 376 | myForceReconnect = false; |
|
| 325 | } |
377 | } |
| 326 | 378 | ||
| 327 | void AsyncSocket::close() |
379 | void AsyncSocket::close() |
| 328 | { |
380 | { |
| 329 | if (mySocket != -1) |
381 | if (mySocket != -1) |
| 330 | { |
382 | { |
| 331 | ::close(mySocket); |
383 | ::close(mySocket); |
| 332 | mySocket = -1; |
384 | mySocket = -1; |
| 333 | setEvent(ASYNCSOCKET_EVENT_CLOSED); |
385 | setEvent(ASYNCSOCKET_EVENT_CLOSED); |
| 334 | } |
386 | } |
| 335 | } |
387 | } |
| 336 | 388 | ||
| 337 | void AsyncSocket::setReconnectTimeout(unsigned int timeout) |
389 | void AsyncSocket::setReconnectTimeout(unsigned int timeout) |
| 338 | { |
390 | { |
| 339 | myReconnectTimeout = timeout; |
391 | myReconnectTimeout = timeout; |
| 340 | } |
392 | } |
| 341 | 393 | ||
| 342 | void AsyncSocket::startEvent() |
394 | void AsyncSocket::startEvent() |
| 343 | { |
395 | { |
| Line 347... | Line 399... | ||
| 347 | int AsyncSocket::getEvent() |
399 | int AsyncSocket::getEvent() |
| 348 | { |
400 | { |
| 349 | int event = myEvent; |
401 | int event = myEvent; |
| 350 | myEvent = ASYNCSOCKET_EVENT_NONE; |
402 | myEvent = ASYNCSOCKET_EVENT_NONE; |
| 351 | return event; |
403 | return event; |
| 352 | } |
404 | } |
| 353 | 405 | ||
| 354 | void AsyncSocket::waitForEvent() |
406 | void AsyncSocket::waitForEvent() |
| 355 | { |
407 | { |
| 356 | myEventSemaphore.wait(); |
408 | myEventSemaphore.wait(); |
| 357 | } |
409 | } |
| 358 | 410 | ||
| 359 | void AsyncSocket::stopEvent() |
411 | void AsyncSocket::stopEvent() |
| 360 | { |
412 | { |
| 361 | myEventSemaphore.unlock(); |
413 | myEventSemaphore.unlock(); |
| 362 | } |
414 | } |
| 363 | 415 | ||
| 364 | void AsyncSocket::setAddress(string address, int port) |
416 | void AsyncSocket::setAddress(string address, int port) |
| 365 | { |
417 | { |
| 366 | myAddress = address; |
418 | myAddress = address; |
| 367 | myPort = port; |
419 | myPort = port; |
| Line 388... | Line 440... | ||
| 388 | { |
440 | { |
| 389 | myEventSemaphore.lock(); |
441 | myEventSemaphore.lock(); |
| 390 | myEvent = event; |
442 | myEvent = event; |
| 391 | myEventSemaphore.unlock(); |
443 | myEventSemaphore.unlock(); |
| 392 | myEventSemaphore.broadcast(); |
444 | myEventSemaphore.broadcast(); |
| - | 445 | } |
|
| - | 446 | ||
| - | 447 | void AsyncSocket::forceReconnect() |
|
| - | 448 | { |
|
| - | 449 | myForceReconnect = true; |
|
| - | 450 | mySemaphore.broadcast(); |
|
| 393 | } |
451 | } |
| 394 | 452 | ||
| 395 | void AsyncSocket::signalHandler(int signum) |
453 | void AsyncSocket::signalHandler(int signum) |
| 396 | { |
454 | { |
| - | 455 | //FIXME: We must know which socket is ready to read by using select... |
|
| 397 | //cout << "DEBUG: signalHandler signum: " << signum << endl; |
456 | //cout << "DEBUG: signalHandler signum: " << signum << endl; |
| 398 | mySemaphore.broadcast(); |
457 | mySemaphore.broadcast(); |
| 399 | } |
458 | } |