Subversion Repositories HomeAutomation

Rev

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