Subversion Repositories HomeAutomation

Rev

Rev 975 | Rev 981 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
969 runge 1
/***************************************************************************
2
 *   Copyright (C) December 6, 2008 by Mattias Runge                             *
3
 *   mattias@runge.se                                                      *
4
 *   asyncsocket.cpp                                            *
5
 *                                                                         *
6
 *   This program is free software; you can redistribute it and/or modify  *
7
 *   it under the terms of the GNU General Public License as published by  *
8
 *   the Free Software Foundation; either version 2 of the License, or     *
9
 *   (at your option) any later version.                                   *
10
 *                                                                         *
11
 *   This program is distributed in the hope that it will be useful,       *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU General Public License for more details.                          *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU General Public License     *
17
 *   along with this program; if not, write to the                         *
18
 *   Free Software Foundation, Inc.,                                       *
19
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20
 ***************************************************************************/
21
 
22
#include "asyncsocket.h"
976 runge 23
 
969 runge 24
Semaphore AsyncSocket::mySemaphore;
25
 
26
AsyncSocket::AsyncSocket()
27
{
28
    mySocket = -1;
976 runge 29
    myReconnectTimeout = 0;
974 runge 30
    myForceReconnect = false;
969 runge 31
 
32
    Thread<AsyncSocket>();
33
}
34
 
35
AsyncSocket::~AsyncSocket()
36
{
37
    if (mySocket != -1)
38
    {
39
        ::close(mySocket);
40
        mySocket = -1;
41
    }
42
 
43
    stop();
44
}
45
 
46
void AsyncSocket::run()
47
{
48
    SyslogStream &slog = SyslogStream::getInstance();
49
 
976 runge 50
    if (myReconnectTimeout == 0)
51
    {
52
        connect();
53
    }
54
    else
55
    {
56
        reconnectLoop();
57
    }
969 runge 58
 
59
    char buf[MAXBUFFER + 1];
60
    string data;
61
    int status;
974 runge 62
    int rc;
63
    int timeSince = time(NULL) + 10;
64
 
976 runge 65
 
969 runge 66
 
67
    try
68
    {
69
        while (1)
70
        {
976 runge 71
            AsyncSocket::mySemaphore.lock();
969 runge 72
 
976 runge 73
            if (myReconnectTimeout == 0)
74
            {
75
                rc = mySemaphore.wait();
76
            }
77
            else
78
            {
79
                rc = mySemaphore.wait(10);
80
            }
81
 
82
            AsyncSocket::mySemaphore.unlock();
83
 
974 runge 84
            if (myForceReconnect)
85
            {
86
                slog << "Disconnected from server.\n";
87
                reconnectLoop();
88
                timeSince = time(NULL) + 10;
89
                continue;
90
            }
91
 
969 runge 92
            //cout << "Socket awoken...\n";
974 runge 93
 
94
            if (rc == ETIMEDOUT)
95
            {
96
                /* Socket timed out, this means we have not received anything in some time
97
                and we should check the connection */
969 runge 98
 
974 runge 99
                setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
100
                timeSince = time(NULL);
101
            }
102
            else
969 runge 103
            {
974 runge 104
                while (myOutQueue.size() > 0)
105
                {
106
                    data = myOutQueue.pop();
969 runge 107
 
974 runge 108
                    //slog << "Sending: " << data << "\n";
969 runge 109
 
974 runge 110
                    status = ::send(mySocket, data.c_str(), data.size(), 0);
969 runge 111
 
974 runge 112
                    //slog << "Status: " << status << "\n";
113
 
114
                    if (status == -1)
969 runge 115
                    {
974 runge 116
                        switch (errno)
117
                        {
118
                            case EACCES:
119
                            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).)");
969 runge 120
 
974 runge 121
                            case EAGAIN:
122
                            //slog << "The socket is marked non-blocking and the requested operation would block.\n";
123
                            break;
969 runge 124
 
974 runge 125
                            case EBADF:
126
                            throw new SocketException("An invalid descriptor was specified.");
969 runge 127
 
974 runge 128
                            case ECONNRESET:
129
                            throw new SocketException("Connection reset by peer.");
969 runge 130
 
974 runge 131
                            case EDESTADDRREQ:
132
                            throw new SocketException("The socket is not connection-mode, and no peer address is set.");
969 runge 133
 
974 runge 134
                            case EFAULT:
135
                            throw new SocketException("An invalid user space address was specified for an argument.");
969 runge 136
 
974 runge 137
                            case EINTR:
138
                            throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
969 runge 139
 
974 runge 140
                            case EINVAL:
141
                            throw new SocketException("1Invalid argument passed.");
969 runge 142
 
974 runge 143
                            case EISCONN:
144
                            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.)");
969 runge 145
 
974 runge 146
                            case EMSGSIZE:
147
                            throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.");
969 runge 148
 
974 runge 149
                            case ENOBUFS:
150
                            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.)");
969 runge 151
 
974 runge 152
                            case ENOMEM:
153
                            throw new SocketException("No memory available.");
969 runge 154
 
974 runge 155
                            case ENOTCONN:
156
                            throw new SocketException("The socket is not connected, and no target has been given.");
969 runge 157
 
974 runge 158
                            case ENOTSOCK:
159
                            throw new SocketException("The argument s is not a socket.");
969 runge 160
 
974 runge 161
                            case EOPNOTSUPP:
162
                            throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
969 runge 163
 
974 runge 164
                            case EPIPE:
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.");
969 runge 166
 
974 runge 167
                            default:
976 runge 168
                            slog << "Unknow exception: " + itos(errno) + "\n";
974 runge 169
                            break;
170
                        }
969 runge 171
                    }
172
                }
173
 
974 runge 174
                memset(buf, 0, MAXBUFFER + 1);
969 runge 175
 
974 runge 176
                status = ::recv(mySocket, buf, MAXBUFFER, 0);
969 runge 177
 
974 runge 178
                if (status == -1)
969 runge 179
                {
974 runge 180
                    switch (errno)
181
                    {
182
                        case EAGAIN:
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";
184
                        break;
969 runge 185
 
974 runge 186
                        case EBADF:
187
                        throw new SocketException("The argument s is an invalid descriptor.");
969 runge 188
 
974 runge 189
                        case ECONNREFUSED:
190
                        throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
969 runge 191
 
974 runge 192
                        case EFAULT:
193
                        throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
969 runge 194
 
974 runge 195
                        case EINTR:
196
                        throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
969 runge 197
 
974 runge 198
                        case EINVAL:
199
                        //throw new SocketException("2Invalid argument passed.");
200
                        slog << "Disconnected from server.\n";
201
                        reconnectLoop();
976 runge 202
                        timeSince = time(NULL) + 10;
974 runge 203
                        break;
969 runge 204
 
974 runge 205
                        case ENOMEM:
206
                        throw new SocketException("Could not allocate memory for recvmsg().");
969 runge 207
 
974 runge 208
                        case ENOTCONN:
209
                        throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and  accept(2)).");
969 runge 210
 
974 runge 211
                        case ENOTSOCK:
212
                        throw new SocketException("The argument s does not refer to a socket.");
969 runge 213
 
974 runge 214
                        default:
976 runge 215
                        slog << "Unknow exception: " + itos(errno) + "\n";
974 runge 216
                        break;
217
                    }
969 runge 218
                }
974 runge 219
                else if (status == 0)
220
                {
221
                    slog << "Disconnected from server.\n";
222
                    reconnectLoop();
976 runge 223
                    timeSince = time(NULL);
974 runge 224
                }
225
                else if (status > 0)
226
                {
976 runge 227
                    timeSince = time(NULL) + 10;
228
 
974 runge 229
                    data = buf;
969 runge 230
 
976 runge 231
                    //slog << "Receiving: " + data + "\n";
232
 
974 runge 233
                    myInQueue.push(data);
969 runge 234
 
974 runge 235
                    setEvent(ASYNCSOCKET_EVENT_DATA);
236
                }
237
 
238
                if (timeSince + 10 < time(NULL))
239
                {
240
                    setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
241
                    timeSince = time(NULL);
242
                }
969 runge 243
            }
244
        }
245
    }
246
    catch (SocketException *e)
247
    {
976 runge 248
        slog << "Exception: " + e->getDescription() + "\n";
249
        //mySemaphore.unlock();
969 runge 250
        setEvent(ASYNCSOCKET_EVENT_DIED);
251
        stop();
252
    }
253
 
254
    close();
255
 
976 runge 256
    //mySemaphore.unlock();
969 runge 257
}
258
 
259
void AsyncSocket::reconnectLoop()
260
{
976 runge 261
    if (myReconnectTimeout == 0)
262
    {
263
        throw new SocketException("Connection is closed.");
264
    }
265
 
969 runge 266
    SyslogStream &slog = SyslogStream::getInstance();
267
 
268
    while (1)
269
    {
270
        try
271
        {
272
            connect();
273
            break;
274
        }
275
        catch (SocketException *e)
276
        {
976 runge 277
            slog << "Could not connect: " + e->getDescription() + "\n";
278
            slog << "Will try again in " + itos(myReconnectTimeout) + " seconds\n";
969 runge 279
            sleep(myReconnectTimeout);
280
        }
281
    }
282
}
283
 
284
void AsyncSocket::connect()
285
{
976 runge 286
    SyslogStream &slog = SyslogStream::getInstance();
287
 
975 runge 288
    close();
976 runge 289
 
290
    slog << "Trying to connect...\n";
291
 
969 runge 292
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
293
 
294
    int on = 1;
295
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
296
    if (status == -1)
297
    {
974 runge 298
        throw new SocketException("Connect:Reuseaddress: " + itos(errno));
969 runge 299
    }
300
 
974 runge 301
    ///FIXME: Verify that this works
302
    status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on));
303
    if (status == -1)
304
    {
305
        throw new SocketException("Connect:Keepalive: " + itos(errno));
306
    }
307
 
308
    ///FIXME: Verify that this works
309
    status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on));
310
    if (status == -1)
311
    {
312
        throw new SocketException("Connect:Keepidle: " + itos(errno));
313
    }
314
 
969 runge 315
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
316
 
317
    myAddressStruct.sin_family = AF_INET;
318
    myAddressStruct.sin_port = htons(myPort);
319
 
976 runge 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
    /*
969 runge 328
    status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
329
 
330
    if (status == -1)
331
    {
332
        if (errno == EAFNOSUPPORT)
333
            throw new SocketException("Connect: EAFNOSUPPORT");
334
    }
976 runge 335
    */
336
 
969 runge 337
 
338
    status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
339
 
340
    if (status == -1)
341
    {
342
        switch (errno)
343
        {
344
            case EACCES:
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).)");
346
 
347
            case EPERM:
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.");
349
 
350
            case EADDRINUSE:
351
            throw new SocketException("Local address is already in use.");
352
 
353
            case EAFNOSUPPORT:
354
            throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
355
 
356
            case EAGAIN:
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.");
358
 
359
            case EALREADY:
360
            throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
361
 
362
            case EBADF:
363
            throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
364
 
365
            case ECONNREFUSED:
366
            throw new SocketException("No-one listening on the remote address.");
367
 
368
            case EFAULT:
369
            throw new SocketException("The socket structure address is outside the user's address space.");
370
 
371
            case EINPROGRESS:
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).");
373
 
374
            case EINTR:
375
            throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
376
 
377
            case EISCONN:
378
            throw new SocketException("The socket is already connected.");
379
 
380
            case ENETUNREACH:
381
            throw new SocketException("Network is unreachable.");
382
 
383
            case ENOTSOCK:
384
            throw new SocketException("The file descriptor is not associated with a socket.");
385
 
386
            case ETIMEDOUT:
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.");
388
 
389
            default:
390
            throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
391
        }
392
    }
393
 
394
    struct sigaction saio;
395
    saio.sa_handler = AsyncSocket::signalHandler;
396
    sigemptyset(&saio.sa_mask);
397
    saio.sa_flags = 0;
398
    saio.sa_restorer = NULL;
399
    sigaction(SIGIO, &saio, NULL);
400
 
401
    fcntl(mySocket, F_SETOWN, getpid());
402
    int flags = fcntl(mySocket, F_GETFL);
403
 
404
    if (flags < 0)
405
        throw new SocketException("Async socket fcntl failed");
406
 
407
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
974 runge 408
 
409
    myForceReconnect = false;
976 runge 410
 
411
    slog << "Connection established.\n";
969 runge 412
}
413
 
414
void AsyncSocket::close()
415
{
416
    if (mySocket != -1)
417
    {
418
        ::close(mySocket);
419
        mySocket = -1;
420
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
421
    }
422
}
423
 
424
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
425
{
426
    myReconnectTimeout = timeout;
427
}
428
 
429
void AsyncSocket::startEvent()
430
{
431
    myEventSemaphore.lock();
432
}
433
 
434
int AsyncSocket::getEvent()
435
{
436
    int event = myEvent;
437
    myEvent = ASYNCSOCKET_EVENT_NONE;
438
    return event;
439
}
440
 
441
void AsyncSocket::waitForEvent()
442
{
443
    myEventSemaphore.wait();
444
}
445
 
446
void AsyncSocket::stopEvent()
447
{
448
    myEventSemaphore.unlock();
449
}
450
 
451
void AsyncSocket::setAddress(string address, int port)
452
{
453
    myAddress = address;
454
    myPort = port;
455
}
456
 
457
bool AsyncSocket::availableData()
458
{
459
    return (myInQueue.size() > 0);
460
}
461
 
462
string AsyncSocket::getData()
463
{
464
    return myInQueue.pop();
465
}
466
 
467
bool AsyncSocket::sendData(string data)
468
{
469
    myOutQueue.push(data);
470
    mySemaphore.broadcast();
471
    return true;
472
}
473
 
474
void AsyncSocket::setEvent(int event)
475
{
476
    myEventSemaphore.lock();
477
    myEvent = event;
478
    myEventSemaphore.unlock();
479
    myEventSemaphore.broadcast();
480
}
481
 
974 runge 482
void AsyncSocket::forceReconnect()
483
{
484
    myForceReconnect = true;
485
    mySemaphore.broadcast();
486
}
487
 
969 runge 488
void AsyncSocket::signalHandler(int signum)
489
{
974 runge 490
    //FIXME: We must know which socket is ready to read by using select... 
969 runge 491
    //cout << "DEBUG: signalHandler signum: " << signum << endl;
492
    mySemaphore.broadcast();
493
}