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
 
981 runge 22
#include <map>
23
 
24
 
25
#include <vector>
26
 
27
 
969 runge 28
#include "asyncsocket.h"
976 runge 29
 
981 runge 30
map<string, AsyncSocket*> AsyncSocket::mySockets;
31
Mutex AsyncSocket::mySocketsMutex;
969 runge 32
 
33
AsyncSocket::AsyncSocket()
34
{
981 runge 35
    myId = itos(time(NULL)) + itos(mySockets.size());
36
 
37
    mySockets[myId] = this;
38
 
39
    cout << "DEBUG: Started " << myId << endl;
40
 
969 runge 41
    mySocket = -1;
976 runge 42
    myReconnectTimeout = 0;
974 runge 43
    myForceReconnect = false;
969 runge 44
 
981 runge 45
 
969 runge 46
    Thread<AsyncSocket>();
47
}
48
 
49
AsyncSocket::~AsyncSocket()
50
{
981 runge 51
    mySemaphore.unlock();
52
 
969 runge 53
    if (mySocket != -1)
54
    {
55
        ::close(mySocket);
56
        mySocket = -1;
57
    }
981 runge 58
 
59
    mySockets.erase(myId);
60
 
969 runge 61
    stop();
62
}
63
 
64
void AsyncSocket::run()
65
{
66
    SyslogStream &slog = SyslogStream::getInstance();
67
 
981 runge 68
    if (!isConnected())
976 runge 69
    {
981 runge 70
        if (myReconnectTimeout == 0)
71
        {
72
            connect();
73
        }
74
        else
75
        {
76
            reconnectLoop();
77
        }
976 runge 78
    }
969 runge 79
 
80
    char buf[MAXBUFFER + 1];
81
    string data;
82
    int status;
974 runge 83
    int rc;
84
    int timeSince = time(NULL) + 10;
969 runge 85
 
86
    try
87
    {
88
        while (1)
89
        {
976 runge 90
            AsyncSocket::mySemaphore.lock();
969 runge 91
 
981 runge 92
            rc = mySemaphore.wait(10);
976 runge 93
 
94
            AsyncSocket::mySemaphore.unlock();
95
 
974 runge 96
            if (myForceReconnect)
97
            {
98
                slog << "Disconnected from server.\n";
99
                reconnectLoop();
100
                timeSince = time(NULL) + 10;
101
                continue;
102
            }
103
 
969 runge 104
            //cout << "Socket awoken...\n";
974 runge 105
 
106
            if (rc == ETIMEDOUT)
107
            {
108
                /* Socket timed out, this means we have not received anything in some time
109
                and we should check the connection */
969 runge 110
 
974 runge 111
                setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
112
                timeSince = time(NULL);
113
            }
114
            else
969 runge 115
            {
974 runge 116
                while (myOutQueue.size() > 0)
117
                {
118
                    data = myOutQueue.pop();
969 runge 119
 
981 runge 120
                    sendDataDirect(data);
969 runge 121
                }
122
 
974 runge 123
                memset(buf, 0, MAXBUFFER + 1);
969 runge 124
 
974 runge 125
                status = ::recv(mySocket, buf, MAXBUFFER, 0);
969 runge 126
 
974 runge 127
                if (status == -1)
969 runge 128
                {
974 runge 129
                    switch (errno)
130
                    {
131
                        case EAGAIN:
132
                        //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";
133
                        break;
969 runge 134
 
974 runge 135
                        case EBADF:
136
                        throw new SocketException("The argument s is an invalid descriptor.");
969 runge 137
 
974 runge 138
                        case ECONNREFUSED:
139
                        throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
969 runge 140
 
974 runge 141
                        case EFAULT:
142
                        throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
969 runge 143
 
974 runge 144
                        case EINTR:
145
                        throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
969 runge 146
 
974 runge 147
                        case EINVAL:
148
                        //throw new SocketException("2Invalid argument passed.");
149
                        slog << "Disconnected from server.\n";
150
                        reconnectLoop();
976 runge 151
                        timeSince = time(NULL) + 10;
974 runge 152
                        break;
969 runge 153
 
974 runge 154
                        case ENOMEM:
155
                        throw new SocketException("Could not allocate memory for recvmsg().");
969 runge 156
 
974 runge 157
                        case ENOTCONN:
158
                        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 159
 
974 runge 160
                        case ENOTSOCK:
161
                        throw new SocketException("The argument s does not refer to a socket.");
969 runge 162
 
974 runge 163
                        default:
976 runge 164
                        slog << "Unknow exception: " + itos(errno) + "\n";
974 runge 165
                        break;
166
                    }
969 runge 167
                }
974 runge 168
                else if (status == 0)
169
                {
170
                    slog << "Disconnected from server.\n";
171
                    reconnectLoop();
976 runge 172
                    timeSince = time(NULL);
974 runge 173
                }
174
                else if (status > 0)
175
                {
976 runge 176
                    timeSince = time(NULL) + 10;
177
 
974 runge 178
                    data = buf;
969 runge 179
 
976 runge 180
                    //slog << "Receiving: " + data + "\n";
181
 
974 runge 182
                    myInQueue.push(data);
969 runge 183
 
974 runge 184
                    setEvent(ASYNCSOCKET_EVENT_DATA);
185
                }
186
 
187
                if (timeSince + 10 < time(NULL))
188
                {
189
                    setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
190
                    timeSince = time(NULL);
191
                }
969 runge 192
            }
193
        }
194
    }
195
    catch (SocketException *e)
196
    {
976 runge 197
        slog << "Exception: " + e->getDescription() + "\n";
198
        //mySemaphore.unlock();
969 runge 199
        setEvent(ASYNCSOCKET_EVENT_DIED);
200
        stop();
201
    }
202
 
203
    close();
204
 
976 runge 205
    //mySemaphore.unlock();
969 runge 206
}
207
 
208
void AsyncSocket::reconnectLoop()
209
{
976 runge 210
    if (myReconnectTimeout == 0)
211
    {
981 runge 212
        close();
976 runge 213
        throw new SocketException("Connection is closed.");
214
    }
215
 
969 runge 216
    SyslogStream &slog = SyslogStream::getInstance();
217
 
218
    while (1)
219
    {
220
        try
221
        {
222
            connect();
223
            break;
224
        }
225
        catch (SocketException *e)
226
        {
976 runge 227
            slog << "Could not connect: " + e->getDescription() + "\n";
228
            slog << "Will try again in " + itos(myReconnectTimeout) + " seconds\n";
969 runge 229
            sleep(myReconnectTimeout);
230
        }
231
    }
232
}
233
 
981 runge 234
void AsyncSocket::create()
969 runge 235
{
975 runge 236
    close();
976 runge 237
 
969 runge 238
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
239
 
240
    int on = 1;
241
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
242
    if (status == -1)
243
    {
981 runge 244
        close();
245
        throw new SocketException("Create:Reuseaddress: " + itos(errno));
969 runge 246
    }
981 runge 247
}
969 runge 248
 
981 runge 249
void AsyncSocket::startListen()
250
{
251
    create();
252
 
253
    myAddressStruct.sin_family = AF_INET;
254
    myAddressStruct.sin_addr.s_addr = INADDR_ANY;
255
    myAddressStruct.sin_port = htons(myPort);
256
 
257
    int status = ::bind(mySocket, (struct sockaddr *)&myAddressStruct, sizeof(myAddressStruct));
258
 
259
    if (status == -1)
260
    {
261
        close();
262
        switch (errno)
263
        {
264
            case EACCES:
265
            throw new SocketException("The address is protected, and the user is not the superuser.");
266
 
267
            case EADDRINUSE:
268
            throw new SocketException("The given address is already in use.");
269
 
270
            case EBADF:
271
            throw new SocketException("sockfd is not a valid descriptor.");
272
 
273
            case EINVAL:
274
            throw new SocketException("The socket is already bound to an address.");
275
 
276
            case ENOTSOCK:
277
            throw new SocketException("sockfd is a descriptor for a file, not a socket.");
278
 
279
            //case EACCES:
280
            //throw new SocketException("Search permission is denied on a component of the path prefix. (See also path_resolution(7).)");
281
 
282
            case EADDRNOTAVAIL:
283
            throw new SocketException("A nonexistent interface was requested or the requested address was not local.");
284
 
285
            case EFAULT:
286
            throw new SocketException("addr points outside the user's accessible address space.");
287
 
288
            //case EINVAL:
289
            //throw new SocketException("The addrlen is wrong, or the socket was not in the AF_UNIX family.");
290
 
291
            case ELOOP:
292
            throw new SocketException("Too many symbolic links were encountered in resolving addr.");
293
 
294
            case ENAMETOOLONG:
295
            throw new SocketException("addr is too long.");
296
 
297
            case ENOENT:
298
            throw new SocketException("The file does not exist.");
299
 
300
            case ENOMEM:
301
            throw new SocketException("Insufficient kernel memory was available.");
302
 
303
            case ENOTDIR:
304
            throw new SocketException("A component of the path prefix is not a directory.");
305
 
306
            case EROFS:
307
            throw new SocketException("The socket inode would reside on a read-only file system.");
308
 
309
            default:
310
            throw new SocketException("Unknow exception: " + itos(errno));
311
            break;
312
        }
313
    }
314
 
315
    status = ::listen(mySocket, MAXCONNECTIONS);
316
 
317
    if (status == -1)
318
    {
319
        close();
320
        switch (errno)
321
        {
322
            case EADDRINUSE:
323
            throw new SocketException("Another socket is already listening on the same port.");
324
 
325
            case EBADF:
326
            throw new SocketException("The argument sockfd is not a valid descriptor.");
327
 
328
            case ENOTSOCK:
329
            throw new SocketException("The argument sockfd is not a socket.");
330
 
331
            case EOPNOTSUPP:
332
            throw new SocketException("The socket is not of a type that supports the listen() operation.");
333
 
334
            default:
335
            throw new SocketException("Unknow exception: " + itos(errno));
336
            break;
337
        }
338
    }
339
}
340
 
341
bool AsyncSocket::accept(AsyncSocket* newSocket)
342
{
343
    int addr_length = sizeof(myAddressStruct);
344
    int socket = ::accept(mySocket, (sockaddr*)&myAddressStruct, (socklen_t*)&addr_length);
345
 
346
    if (socket > 0)
347
    {
348
        newSocket->setSocket(socket);
349
        return true;
350
    }
351
 
352
    return false;
353
}
354
 
355
void AsyncSocket::connect()
356
{
357
    SyslogStream &slog = SyslogStream::getInstance();
358
 
359
    create();
360
 
361
    int on = 1;
974 runge 362
    ///FIXME: Verify that this works
981 runge 363
    int status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on));
974 runge 364
    if (status == -1)
365
    {
981 runge 366
        close();
974 runge 367
        throw new SocketException("Connect:Keepalive: " + itos(errno));
368
    }
369
 
370
    ///FIXME: Verify that this works
371
    status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on));
372
    if (status == -1)
373
    {
981 runge 374
        close();
974 runge 375
        throw new SocketException("Connect:Keepidle: " + itos(errno));
376
    }
377
 
969 runge 378
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
379
 
380
    myAddressStruct.sin_family = AF_INET;
381
    myAddressStruct.sin_port = htons(myPort);
382
 
976 runge 383
    struct hostent *hptr = gethostbyname(myAddress.c_str());
384
    if (hptr == NULL)
385
    {
981 runge 386
        close();
976 runge 387
        throw new SocketException("Connect: Could not resolv ip address");
388
    }
389
 
390
    memcpy(&myAddressStruct.sin_addr, hptr->h_addr, hptr->h_length);
391
    /*
969 runge 392
    status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
393
 
394
    if (status == -1)
395
    {
396
        if (errno == EAFNOSUPPORT)
397
            throw new SocketException("Connect: EAFNOSUPPORT");
398
    }
976 runge 399
    */
400
 
981 runge 401
    slog << "Trying to connect...\n";
402
 
969 runge 403
    status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
404
 
405
    if (status == -1)
406
    {
981 runge 407
        close();
969 runge 408
        switch (errno)
409
        {
410
            case EACCES:
411
            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).)");
412
 
413
            case EPERM:
414
            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.");
415
 
416
            case EADDRINUSE:
417
            throw new SocketException("Local address is already in use.");
418
 
419
            case EAFNOSUPPORT:
420
            throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
421
 
422
            case EAGAIN:
423
            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.");
424
 
425
            case EALREADY:
426
            throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
427
 
428
            case EBADF:
429
            throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
430
 
431
            case ECONNREFUSED:
432
            throw new SocketException("No-one listening on the remote address.");
433
 
434
            case EFAULT:
435
            throw new SocketException("The socket structure address is outside the user's address space.");
436
 
437
            case EINPROGRESS:
438
            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).");
439
 
440
            case EINTR:
441
            throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
442
 
443
            case EISCONN:
444
            throw new SocketException("The socket is already connected.");
445
 
446
            case ENETUNREACH:
447
            throw new SocketException("Network is unreachable.");
448
 
449
            case ENOTSOCK:
450
            throw new SocketException("The file descriptor is not associated with a socket.");
451
 
452
            case ETIMEDOUT:
453
            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.");
454
 
455
            default:
456
            throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
457
        }
458
    }
459
 
460
    struct sigaction saio;
461
    saio.sa_handler = AsyncSocket::signalHandler;
462
    sigemptyset(&saio.sa_mask);
463
    saio.sa_flags = 0;
464
    saio.sa_restorer = NULL;
465
    sigaction(SIGIO, &saio, NULL);
466
 
467
    fcntl(mySocket, F_SETOWN, getpid());
468
    int flags = fcntl(mySocket, F_GETFL);
469
 
470
    if (flags < 0)
981 runge 471
    {
472
        close();
969 runge 473
        throw new SocketException("Async socket fcntl failed");
981 runge 474
    }
969 runge 475
 
476
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
974 runge 477
 
478
    myForceReconnect = false;
976 runge 479
 
480
    slog << "Connection established.\n";
969 runge 481
}
482
 
483
void AsyncSocket::close()
484
{
485
    if (mySocket != -1)
486
    {
487
        ::close(mySocket);
488
        mySocket = -1;
489
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
490
    }
491
}
492
 
981 runge 493
bool AsyncSocket::isConnected()
494
{
495
    return mySocket != -1;
496
}
497
 
969 runge 498
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
499
{
500
    myReconnectTimeout = timeout;
501
}
502
 
503
void AsyncSocket::startEvent()
504
{
505
    myEventSemaphore.lock();
506
}
507
 
508
int AsyncSocket::getEvent()
509
{
510
    int event = myEvent;
511
    myEvent = ASYNCSOCKET_EVENT_NONE;
512
    return event;
513
}
514
 
515
void AsyncSocket::waitForEvent()
516
{
517
    myEventSemaphore.wait();
518
}
519
 
520
void AsyncSocket::stopEvent()
521
{
522
    myEventSemaphore.unlock();
523
}
524
 
525
void AsyncSocket::setAddress(string address, int port)
526
{
527
    myAddress = address;
528
    myPort = port;
529
}
530
 
981 runge 531
void AsyncSocket::setPort(int port)
532
{
533
    myPort = port;
534
}
535
 
536
void AsyncSocket::setSocket(int socket)
537
{
538
    mySocket = socket;
539
}
540
 
541
int AsyncSocket::getSocket()
542
{
543
    return mySocket;
544
}
545
 
969 runge 546
bool AsyncSocket::availableData()
547
{
548
    return (myInQueue.size() > 0);
549
}
550
 
551
string AsyncSocket::getData()
552
{
553
    return myInQueue.pop();
554
}
555
 
556
bool AsyncSocket::sendData(string data)
557
{
558
    myOutQueue.push(data);
559
    mySemaphore.broadcast();
560
    return true;
561
}
562
 
981 runge 563
void AsyncSocket::sendDataDirect(string data)
564
{
565
    SyslogStream &slog = SyslogStream::getInstance();
566
 
567
    //slog << "Sending: " << data << "\n";
568
 
569
    int status = ::send(mySocket, data.c_str(), data.size(), 0);
570
 
571
    //slog << "Status: " << status << "\n";
572
 
573
    if (status == -1)
574
    {
575
        switch (errno)
576
        {
577
            case EACCES:
578
            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).)");
579
 
580
            case EAGAIN:
581
            //slog << "The socket is marked non-blocking and the requested operation would block.\n";
582
            break;
583
 
584
            case EBADF:
585
            throw new SocketException("An invalid descriptor was specified.");
586
 
587
            case ECONNRESET:
588
            throw new SocketException("Connection reset by peer.");
589
 
590
            case EDESTADDRREQ:
591
            throw new SocketException("The socket is not connection-mode, and no peer address is set.");
592
 
593
            case EFAULT:
594
            throw new SocketException("An invalid user space address was specified for an argument.");
595
 
596
            case EINTR:
597
            throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
598
 
599
            case EINVAL:
600
            throw new SocketException("1Invalid argument passed.");
601
 
602
            case EISCONN:
603
            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.)");
604
 
605
            case EMSGSIZE:
606
            throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.");
607
 
608
            case ENOBUFS:
609
            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.)");
610
 
611
            case ENOMEM:
612
            throw new SocketException("No memory available.");
613
 
614
            case ENOTCONN:
615
            throw new SocketException("The socket is not connected, and no target has been given.");
616
 
617
            case ENOTSOCK:
618
            throw new SocketException("The argument s is not a socket.");
619
 
620
            case EOPNOTSUPP:
621
            throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
622
 
623
            case EPIPE:
624
            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.");
625
 
626
            default:
627
            slog << "Unknow exception: " + itos(errno) + "\n";
628
            break;
629
        }
630
    }
631
}
632
 
969 runge 633
void AsyncSocket::setEvent(int event)
634
{
635
    myEventSemaphore.lock();
636
    myEvent = event;
637
    myEventSemaphore.unlock();
638
    myEventSemaphore.broadcast();
639
}
640
 
974 runge 641
void AsyncSocket::forceReconnect()
642
{
643
    myForceReconnect = true;
644
    mySemaphore.broadcast();
645
}
646
 
969 runge 647
void AsyncSocket::signalHandler(int signum)
648
{
974 runge 649
    //FIXME: We must know which socket is ready to read by using select... 
981 runge 650
    cout << "DEBUG: signalHandler signum: " << signum << endl;
651
 
652
    map<string, AsyncSocket*>::iterator iter;
653
 
654
    mySocketsMutex.lock();
655
    for (iter = mySockets.begin(); iter != mySockets.end(); iter++)///FIXME: We should check order and length
656
    {
657
        cout << "DEBUG: signalHandler: calling: " << iter->second->getId() << endl;
658
        iter->second->mySemaphore.broadcast();
659
    }
660
    mySocketsMutex.unlock();
661
 
969 runge 662
}