Subversion Repositories HomeAutomation

Rev

Rev 975 | Rev 981 | 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 = 10;
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
            rc = mySemaphore.wait(5);
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: " << errno << "\n";
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: " << errno << "\n";
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: " << e->getDescription() << "\n";
248
        slog << "Exception: " + e->getDescription() + "\n";
229
        mySemaphore.unlock();
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.unlock();
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: " << e->getDescription() << "\n";
277
            slog << "Could not connect: " + e->getDescription() + "\n";
255
            slog << "Will try again in " << myReconnectTimeout << " seconds\n";
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)