Subversion Repositories HomeAutomation

Rev

Go to most recent revision | Details | 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"
23
#include <iostream>
24
Semaphore AsyncSocket::mySemaphore;
25
 
26
AsyncSocket::AsyncSocket()
27
{
28
    mySocket = -1;
29
    myReconnectTimeout = 10;
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
 
50
    reconnectLoop();
51
 
52
    char buf[MAXBUFFER + 1];
53
    string data;
54
    int status;
974 runge 55
    int rc;
56
    int timeSince = time(NULL) + 10;
57
 
969 runge 58
    AsyncSocket::mySemaphore.lock();
59
 
60
    try
61
    {
62
        while (1)
63
        {
974 runge 64
            rc = mySemaphore.wait(5);
969 runge 65
 
974 runge 66
            if (myForceReconnect)
67
            {
68
                slog << "Disconnected from server.\n";
69
                reconnectLoop();
70
                timeSince = time(NULL) + 10;
71
                continue;
72
            }
73
 
969 runge 74
            //cout << "Socket awoken...\n";
974 runge 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 */
969 runge 80
 
974 runge 81
                setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
82
                timeSince = time(NULL);
83
            }
84
            else
969 runge 85
            {
974 runge 86
                while (myOutQueue.size() > 0)
87
                {
88
                    data = myOutQueue.pop();
969 runge 89
 
974 runge 90
                    //slog << "Sending: " << data << "\n";
969 runge 91
 
974 runge 92
                    status = ::send(mySocket, data.c_str(), data.size(), 0);
969 runge 93
 
974 runge 94
                    //slog << "Status: " << status << "\n";
95
 
96
                    if (status == -1)
969 runge 97
                    {
974 runge 98
                        switch (errno)
99
                        {
100
                            case EACCES:
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).)");
969 runge 102
 
974 runge 103
                            case EAGAIN:
104
                            //slog << "The socket is marked non-blocking and the requested operation would block.\n";
105
                            break;
969 runge 106
 
974 runge 107
                            case EBADF:
108
                            throw new SocketException("An invalid descriptor was specified.");
969 runge 109
 
974 runge 110
                            case ECONNRESET:
111
                            throw new SocketException("Connection reset by peer.");
969 runge 112
 
974 runge 113
                            case EDESTADDRREQ:
114
                            throw new SocketException("The socket is not connection-mode, and no peer address is set.");
969 runge 115
 
974 runge 116
                            case EFAULT:
117
                            throw new SocketException("An invalid user space address was specified for an argument.");
969 runge 118
 
974 runge 119
                            case EINTR:
120
                            throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
969 runge 121
 
974 runge 122
                            case EINVAL:
123
                            throw new SocketException("1Invalid argument passed.");
969 runge 124
 
974 runge 125
                            case EISCONN:
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.)");
969 runge 127
 
974 runge 128
                            case EMSGSIZE:
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.");
969 runge 130
 
974 runge 131
                            case ENOBUFS:
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.)");
969 runge 133
 
974 runge 134
                            case ENOMEM:
135
                            throw new SocketException("No memory available.");
969 runge 136
 
974 runge 137
                            case ENOTCONN:
138
                            throw new SocketException("The socket is not connected, and no target has been given.");
969 runge 139
 
974 runge 140
                            case ENOTSOCK:
141
                            throw new SocketException("The argument s is not a socket.");
969 runge 142
 
974 runge 143
                            case EOPNOTSUPP:
144
                            throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
969 runge 145
 
974 runge 146
                            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.");
969 runge 148
 
974 runge 149
                            default:
150
                            slog << "Unknow exception: " << errno << "\n";
151
                            break;
152
                        }
969 runge 153
                    }
154
                }
155
 
974 runge 156
                memset(buf, 0, MAXBUFFER + 1);
969 runge 157
 
974 runge 158
                status = ::recv(mySocket, buf, MAXBUFFER, 0);
969 runge 159
 
974 runge 160
                //slog << "Receiving: " << buf << "\n";
161
 
162
                if (status == -1)
969 runge 163
                {
974 runge 164
                    switch (errno)
165
                    {
166
                        case EAGAIN:
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";
168
                        break;
969 runge 169
 
974 runge 170
                        case EBADF:
171
                        throw new SocketException("The argument s is an invalid descriptor.");
969 runge 172
 
974 runge 173
                        case ECONNREFUSED:
174
                        throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
969 runge 175
 
974 runge 176
                        case EFAULT:
177
                        throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
969 runge 178
 
974 runge 179
                        case EINTR:
180
                        throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
969 runge 181
 
974 runge 182
                        case EINVAL:
183
                        //throw new SocketException("2Invalid argument passed.");
184
                        slog << "Disconnected from server.\n";
185
                        reconnectLoop();
186
                        break;
969 runge 187
 
974 runge 188
                        case ENOMEM:
189
                        throw new SocketException("Could not allocate memory for recvmsg().");
969 runge 190
 
974 runge 191
                        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)).");
969 runge 193
 
974 runge 194
                        case ENOTSOCK:
195
                        throw new SocketException("The argument s does not refer to a socket.");
969 runge 196
 
974 runge 197
                        default:
198
                        slog << "Unknow exception: " << errno << "\n";
199
                        break;
200
                    }
969 runge 201
                }
974 runge 202
                else if (status == 0)
203
                {
204
                    slog << "Disconnected from server.\n";
205
                    reconnectLoop();
206
                }
207
                else if (status > 0)
208
                {
209
                    data = buf;
969 runge 210
 
974 runge 211
                    myInQueue.push(data);
969 runge 212
 
974 runge 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
                }
969 runge 223
            }
224
        }
225
    }
226
    catch (SocketException *e)
227
    {
228
        slog << "Exception: " << e->getDescription() << "\n";
229
        mySemaphore.unlock();
230
        setEvent(ASYNCSOCKET_EVENT_DIED);
231
        stop();
232
    }
233
 
234
    close();
235
 
236
    mySemaphore.unlock();
237
}
238
 
239
void AsyncSocket::reconnectLoop()
240
{
241
    SyslogStream &slog = SyslogStream::getInstance();
242
 
243
    while (1)
244
    {
245
        try
246
        {
247
            slog << "Trying to connect...\n";
248
            connect();
249
            slog << "Connection established.\n";
250
            break;
251
        }
252
        catch (SocketException *e)
253
        {
254
            slog << "Could not connect: " << e->getDescription() << "\n";
255
            slog << "Will try again in " << myReconnectTimeout << " seconds\n";
256
            sleep(myReconnectTimeout);
257
        }
258
    }
259
}
260
 
261
void AsyncSocket::connect()
262
{
263
    if (mySocket != -1)
264
    {
265
        ::close(mySocket);
266
        mySocket = -1;
267
    }
268
 
269
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
270
 
271
    int on = 1;
272
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
273
    if (status == -1)
274
    {
974 runge 275
        throw new SocketException("Connect:Reuseaddress: " + itos(errno));
969 runge 276
    }
277
 
974 runge 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
    {
289
        throw new SocketException("Connect:Keepidle: " + itos(errno));
290
    }
291
 
969 runge 292
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
293
 
294
    myAddressStruct.sin_family = AF_INET;
295
    myAddressStruct.sin_port = htons(myPort);
296
 
297
    status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
298
 
299
    if (status == -1)
300
    {
301
        if (errno == EAFNOSUPPORT)
302
            throw new SocketException("Connect: EAFNOSUPPORT");
303
    }
304
 
305
    status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
306
 
307
    if (status == -1)
308
    {
309
        switch (errno)
310
        {
311
            case EACCES:
312
            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).)");
313
 
314
            case EPERM:
315
            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.");
316
 
317
            case EADDRINUSE:
318
            throw new SocketException("Local address is already in use.");
319
 
320
            case EAFNOSUPPORT:
321
            throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
322
 
323
            case EAGAIN:
324
            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.");
325
 
326
            case EALREADY:
327
            throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
328
 
329
            case EBADF:
330
            throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
331
 
332
            case ECONNREFUSED:
333
            throw new SocketException("No-one listening on the remote address.");
334
 
335
            case EFAULT:
336
            throw new SocketException("The socket structure address is outside the user's address space.");
337
 
338
            case EINPROGRESS:
339
            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).");
340
 
341
            case EINTR:
342
            throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
343
 
344
            case EISCONN:
345
            throw new SocketException("The socket is already connected.");
346
 
347
            case ENETUNREACH:
348
            throw new SocketException("Network is unreachable.");
349
 
350
            case ENOTSOCK:
351
            throw new SocketException("The file descriptor is not associated with a socket.");
352
 
353
            case ETIMEDOUT:
354
            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.");
355
 
356
            default:
357
            throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
358
        }
359
    }
360
 
361
    struct sigaction saio;
362
    saio.sa_handler = AsyncSocket::signalHandler;
363
    sigemptyset(&saio.sa_mask);
364
    saio.sa_flags = 0;
365
    saio.sa_restorer = NULL;
366
    sigaction(SIGIO, &saio, NULL);
367
 
368
    fcntl(mySocket, F_SETOWN, getpid());
369
    int flags = fcntl(mySocket, F_GETFL);
370
 
371
    if (flags < 0)
372
        throw new SocketException("Async socket fcntl failed");
373
 
374
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
974 runge 375
 
376
    myForceReconnect = false;
969 runge 377
}
378
 
379
void AsyncSocket::close()
380
{
381
    if (mySocket != -1)
382
    {
383
        ::close(mySocket);
384
        mySocket = -1;
385
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
386
    }
387
}
388
 
389
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
390
{
391
    myReconnectTimeout = timeout;
392
}
393
 
394
void AsyncSocket::startEvent()
395
{
396
    myEventSemaphore.lock();
397
}
398
 
399
int AsyncSocket::getEvent()
400
{
401
    int event = myEvent;
402
    myEvent = ASYNCSOCKET_EVENT_NONE;
403
    return event;
404
}
405
 
406
void AsyncSocket::waitForEvent()
407
{
408
    myEventSemaphore.wait();
409
}
410
 
411
void AsyncSocket::stopEvent()
412
{
413
    myEventSemaphore.unlock();
414
}
415
 
416
void AsyncSocket::setAddress(string address, int port)
417
{
418
    myAddress = address;
419
    myPort = port;
420
}
421
 
422
bool AsyncSocket::availableData()
423
{
424
    return (myInQueue.size() > 0);
425
}
426
 
427
string AsyncSocket::getData()
428
{
429
    return myInQueue.pop();
430
}
431
 
432
bool AsyncSocket::sendData(string data)
433
{
434
    myOutQueue.push(data);
435
    mySemaphore.broadcast();
436
    return true;
437
}
438
 
439
void AsyncSocket::setEvent(int event)
440
{
441
    myEventSemaphore.lock();
442
    myEvent = event;
443
    myEventSemaphore.unlock();
444
    myEventSemaphore.broadcast();
445
}
446
 
974 runge 447
void AsyncSocket::forceReconnect()
448
{
449
    myForceReconnect = true;
450
    mySemaphore.broadcast();
451
}
452
 
969 runge 453
void AsyncSocket::signalHandler(int signum)
454
{
974 runge 455
    //FIXME: We must know which socket is ready to read by using select... 
969 runge 456
    //cout << "DEBUG: signalHandler signum: " << signum << endl;
457
    mySemaphore.broadcast();
458
}