Subversion Repositories HomeAutomation

Rev

Rev 1203 | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed

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