Subversion Repositories HomeAutomation

Rev

Rev 974 | Rev 976 | 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"
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
{
975 runge 263
    close();
969 runge 264
 
265
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
266
 
267
    int on = 1;
268
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
269
    if (status == -1)
270
    {
974 runge 271
        throw new SocketException("Connect:Reuseaddress: " + itos(errno));
969 runge 272
    }
273
 
974 runge 274
    ///FIXME: Verify that this works
275
    status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on));
276
    if (status == -1)
277
    {
278
        throw new SocketException("Connect:Keepalive: " + itos(errno));
279
    }
280
 
281
    ///FIXME: Verify that this works
282
    status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on));
283
    if (status == -1)
284
    {
285
        throw new SocketException("Connect:Keepidle: " + itos(errno));
286
    }
287
 
969 runge 288
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
289
 
290
    myAddressStruct.sin_family = AF_INET;
291
    myAddressStruct.sin_port = htons(myPort);
292
 
293
    status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
294
 
295
    if (status == -1)
296
    {
297
        if (errno == EAFNOSUPPORT)
298
            throw new SocketException("Connect: EAFNOSUPPORT");
299
    }
300
 
301
    status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
302
 
303
    if (status == -1)
304
    {
305
        switch (errno)
306
        {
307
            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).)");
309
 
310
            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.");
312
 
313
            case EADDRINUSE:
314
            throw new SocketException("Local address is already in use.");
315
 
316
            case EAFNOSUPPORT:
317
            throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
318
 
319
            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.");
321
 
322
            case EALREADY:
323
            throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
324
 
325
            case EBADF:
326
            throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
327
 
328
            case ECONNREFUSED:
329
            throw new SocketException("No-one listening on the remote address.");
330
 
331
            case EFAULT:
332
            throw new SocketException("The socket structure address is outside the user's address space.");
333
 
334
            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).");
336
 
337
            case EINTR:
338
            throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
339
 
340
            case EISCONN:
341
            throw new SocketException("The socket is already connected.");
342
 
343
            case ENETUNREACH:
344
            throw new SocketException("Network is unreachable.");
345
 
346
            case ENOTSOCK:
347
            throw new SocketException("The file descriptor is not associated with a socket.");
348
 
349
            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.");
351
 
352
            default:
353
            throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
354
        }
355
    }
356
 
357
    struct sigaction saio;
358
    saio.sa_handler = AsyncSocket::signalHandler;
359
    sigemptyset(&saio.sa_mask);
360
    saio.sa_flags = 0;
361
    saio.sa_restorer = NULL;
362
    sigaction(SIGIO, &saio, NULL);
363
 
364
    fcntl(mySocket, F_SETOWN, getpid());
365
    int flags = fcntl(mySocket, F_GETFL);
366
 
367
    if (flags < 0)
368
        throw new SocketException("Async socket fcntl failed");
369
 
370
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
974 runge 371
 
372
    myForceReconnect = false;
969 runge 373
}
374
 
375
void AsyncSocket::close()
376
{
377
    if (mySocket != -1)
378
    {
379
        ::close(mySocket);
380
        mySocket = -1;
381
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
382
    }
383
}
384
 
385
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
386
{
387
    myReconnectTimeout = timeout;
388
}
389
 
390
void AsyncSocket::startEvent()
391
{
392
    myEventSemaphore.lock();
393
}
394
 
395
int AsyncSocket::getEvent()
396
{
397
    int event = myEvent;
398
    myEvent = ASYNCSOCKET_EVENT_NONE;
399
    return event;
400
}
401
 
402
void AsyncSocket::waitForEvent()
403
{
404
    myEventSemaphore.wait();
405
}
406
 
407
void AsyncSocket::stopEvent()
408
{
409
    myEventSemaphore.unlock();
410
}
411
 
412
void AsyncSocket::setAddress(string address, int port)
413
{
414
    myAddress = address;
415
    myPort = port;
416
}
417
 
418
bool AsyncSocket::availableData()
419
{
420
    return (myInQueue.size() > 0);
421
}
422
 
423
string AsyncSocket::getData()
424
{
425
    return myInQueue.pop();
426
}
427
 
428
bool AsyncSocket::sendData(string data)
429
{
430
    myOutQueue.push(data);
431
    mySemaphore.broadcast();
432
    return true;
433
}
434
 
435
void AsyncSocket::setEvent(int event)
436
{
437
    myEventSemaphore.lock();
438
    myEvent = event;
439
    myEventSemaphore.unlock();
440
    myEventSemaphore.broadcast();
441
}
442
 
974 runge 443
void AsyncSocket::forceReconnect()
444
{
445
    myForceReconnect = true;
446
    mySemaphore.broadcast();
447
}
448
 
969 runge 449
void AsyncSocket::signalHandler(int signum)
450
{
974 runge 451
    //FIXME: We must know which socket is ready to read by using select... 
969 runge 452
    //cout << "DEBUG: signalHandler signum: " << signum << endl;
453
    mySemaphore.broadcast();
454
}