Subversion Repositories HomeAutomation

Rev

Rev 969 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 969 Rev 974
Line 25... Line 25...
25
 
25
 
26
AsyncSocket::AsyncSocket()
26
AsyncSocket::AsyncSocket()
27
{
27
{
28
    mySocket = -1;
28
    mySocket = -1;
29
    myReconnectTimeout = 10;
29
    myReconnectTimeout = 10;
-
 
30
    myForceReconnect = false;
30
 
31
 
31
    Thread<AsyncSocket>();
32
    Thread<AsyncSocket>();
32
}
33
}
33
 
34
 
34
AsyncSocket::~AsyncSocket()
35
AsyncSocket::~AsyncSocket()
Line 49... Line 50...
49
    reconnectLoop();
50
    reconnectLoop();
50
 
51
 
51
    char buf[MAXBUFFER + 1];
52
    char buf[MAXBUFFER + 1];
52
    string data;
53
    string data;
53
    int status;
54
    int status;
-
 
55
    int rc;
-
 
56
    int timeSince = time(NULL) + 10;
54
 
57
   
55
    AsyncSocket::mySemaphore.lock();
58
    AsyncSocket::mySemaphore.lock();
56
 
59
 
57
    try
60
    try
58
    {
61
    {
59
        while (1)
62
        while (1)
60
        {
63
        {
61
            mySemaphore.wait();
64
            rc = mySemaphore.wait(5);
-
 
65
 
-
 
66
            if (myForceReconnect)
-
 
67
            {
-
 
68
                slog << "Disconnected from server.\n";
-
 
69
                reconnectLoop();
-
 
70
                timeSince = time(NULL) + 10;
-
 
71
                continue;
-
 
72
            }
62
 
73
 
63
            //cout << "Socket awoken...\n";
74
            //cout << "Socket awoken...\n";
64
 
75
           
65
            while (myOutQueue.size() > 0)
76
            if (rc == ETIMEDOUT)
66
            {
77
            {
-
 
78
                /* Socket timed out, this means we have not received anything in some time
-
 
79
                and we should check the connection */
-
 
80
 
-
 
81
                setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
-
 
82
                timeSince = time(NULL);
-
 
83
            }
-
 
84
            else
-
 
85
            {
-
 
86
                while (myOutQueue.size() > 0)
-
 
87
                {
67
                data = myOutQueue.pop();
88
                    data = myOutQueue.pop();
68
 
89
 
69
                //slog << "Sending: " << data << "\n";
90
                    //slog << "Sending: " << data << "\n";
70
 
91
 
71
                status = ::send(mySocket, data.c_str(), data.size(), 0);
92
                    status = ::send(mySocket, data.c_str(), data.size(), 0);
-
 
93
 
-
 
94
                    //slog << "Status: " << status << "\n";
-
 
95
 
-
 
96
                    if (status == -1)
-
 
97
                    {
-
 
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).)");
-
 
102
 
-
 
103
                            case EAGAIN:
-
 
104
                            //slog << "The socket is marked non-blocking and the requested operation would block.\n";
-
 
105
                            break;
-
 
106
 
-
 
107
                            case EBADF:
-
 
108
                            throw new SocketException("An invalid descriptor was specified.");
-
 
109
 
-
 
110
                            case ECONNRESET:
-
 
111
                            throw new SocketException("Connection reset by peer.");
-
 
112
 
-
 
113
                            case EDESTADDRREQ:
-
 
114
                            throw new SocketException("The socket is not connection-mode, and no peer address is set.");
-
 
115
 
-
 
116
                            case EFAULT:
-
 
117
                            throw new SocketException("An invalid user space address was specified for an argument.");
-
 
118
 
-
 
119
                            case EINTR:
-
 
120
                            throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
-
 
121
 
-
 
122
                            case EINVAL:
-
 
123
                            throw new SocketException("1Invalid argument passed.");
-
 
124
 
-
 
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.)");
-
 
127
 
-
 
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.");
-
 
130
 
-
 
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.)");
-
 
133
 
-
 
134
                            case ENOMEM:
-
 
135
                            throw new SocketException("No memory available.");
-
 
136
 
-
 
137
                            case ENOTCONN:
-
 
138
                            throw new SocketException("The socket is not connected, and no target has been given.");
-
 
139
 
-
 
140
                            case ENOTSOCK:
-
 
141
                            throw new SocketException("The argument s is not a socket.");
-
 
142
 
-
 
143
                            case EOPNOTSUPP:
-
 
144
                            throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
-
 
145
 
-
 
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.");
-
 
148
 
-
 
149
                            default:
-
 
150
                            slog << "Unknow exception: " << errno << "\n";
-
 
151
                            break;
-
 
152
                        }
-
 
153
                    }
-
 
154
                }
-
 
155
 
-
 
156
                memset(buf, 0, MAXBUFFER + 1);
-
 
157
 
-
 
158
                status = ::recv(mySocket, buf, MAXBUFFER, 0);
-
 
159
 
-
 
160
                //slog << "Receiving: " << buf << "\n";
72
 
161
 
73
                if (status == -1)
162
                if (status == -1)
74
                {
163
                {
75
                    switch (errno)
164
                    switch (errno)
76
                    {
165
                    {
77
                        case EACCES:
-
 
78
                        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).)");
-
 
79
 
-
 
80
                        case EAGAIN:
166
                        case EAGAIN:
81
                        //slog << "The socket is marked non-blocking and the requested operation would block.\n";
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";
82
                        break;
168
                        break;
83
 
169
 
84
                        case EBADF:
170
                        case EBADF:
85
                        throw new SocketException("An invalid descriptor was specified.");
171
                        throw new SocketException("The argument s is an invalid descriptor.");
86
 
172
 
87
                        case ECONNRESET:
173
                        case ECONNREFUSED:
88
                        throw new SocketException("Connection reset by peer.");
-
 
89
 
-
 
90
                        case EDESTADDRREQ:
-
 
91
                        throw new SocketException("The socket is not connection-mode, and no peer address is set.");
174
                        throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
92
 
175
 
93
                        case EFAULT:
176
                        case EFAULT:
94
                        throw new SocketException("An invalid user space address was specified for an argument.");
177
                        throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
95
 
178
 
96
                        case EINTR:
179
                        case EINTR:
97
                        throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
180
                        throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
98
 
181
 
99
                        case EINVAL:
182
                        case EINVAL:
100
                        throw new SocketException("Invalid argument passed.");
183
                        //throw new SocketException("2Invalid argument passed.");
101
 
-
 
102
                        case EISCONN:
-
 
103
                        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.)");
-
 
104
 
-
 
105
                        case EMSGSIZE:
184
                        slog << "Disconnected from server.\n";
106
                        throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.");
-
 
107
 
-
 
108
                        case ENOBUFS:
185
                        reconnectLoop();
109
                        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.)");
186
                        break;
110
 
187
 
111
                        case ENOMEM:
188
                        case ENOMEM:
112
                        throw new SocketException("No memory available.");
189
                        throw new SocketException("Could not allocate memory for recvmsg().");
113
 
190
 
114
                        case ENOTCONN:
191
                        case ENOTCONN:
115
                        throw new SocketException("The socket is not connected, and no target has been given.");
192
                        throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and  accept(2)).");
116
 
193
 
117
                        case ENOTSOCK:
194
                        case ENOTSOCK:
118
                        throw new SocketException("The argument s is not a socket.");
195
                        throw new SocketException("The argument s does not refer to a socket.");
119
 
-
 
120
                        case EOPNOTSUPP:
-
 
121
                        throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
-
 
122
 
-
 
123
                        case EPIPE:
-
 
124
                        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.");
-
 
125
 
196
 
126
                        default:
197
                        default:
127
                        slog << "Unknow exception: " << errno << "\n";
198
                        slog << "Unknow exception: " << errno << "\n";
128
                        break;
199
                        break;
129
                    }
200
                    }
130
                }
201
                }
131
            }
-
 
132
 
-
 
133
            memset(buf, 0, MAXBUFFER + 1);
-
 
134
 
-
 
135
            status = ::recv(mySocket, buf, MAXBUFFER, 0);
-
 
136
 
-
 
137
            if (status == -1)
202
                else if (status == 0)
138
            {
-
 
139
                switch (errno)
-
 
140
                {
203
                {
-
 
204
                    slog << "Disconnected from server.\n";
141
                    case EAGAIN:
205
                    reconnectLoop();
-
 
206
                }
142
                    //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";
207
                else if (status > 0)
-
 
208
                {
143
                    break;
209
                    data = buf;
-
 
210
 
-
 
211
                    myInQueue.push(data);
144
 
212
 
145
                    case EBADF:
-
 
146
                    throw new SocketException("The argument s is an invalid descriptor.");
-
 
147
 
-
 
148
                    case ECONNREFUSED:
213
                    setEvent(ASYNCSOCKET_EVENT_DATA);
149
                    throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
-
 
150
 
-
 
151
                    case EFAULT:
-
 
152
                    throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
-
 
153
 
-
 
154
                    case EINTR:
214
                   
155
                    throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
-
 
156
 
-
 
157
                    case EINVAL:
215
                    timeSince = time(NULL);
158
                    throw new SocketException("Invalid argument passed.");
-
 
159
 
-
 
160
                    case ENOMEM:
216
                }
161
                    throw new SocketException("Could not allocate memory for recvmsg().");
-
 
162
 
217
               
163
                    case ENOTCONN:
218
                if (timeSince + 10 < time(NULL))
164
                    throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and  accept(2)).");
-
 
165
 
-
 
166
                    case ENOTSOCK:
-
 
167
                    throw new SocketException("The argument s does not refer to a socket.");
-
 
168
 
-
 
169
                    default:
219
                {
170
                    slog << "Unknow exception: " << errno << "\n";
220
                    setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
171
                    break;
221
                    timeSince = time(NULL);
172
                }
222
                }
173
            }
-
 
174
            else if (status == 0)
-
 
175
            {
-
 
176
                slog << "Disconnected from server.\n";
-
 
177
                reconnectLoop();
-
 
178
            }
-
 
179
            else if (status > 0)
-
 
180
            {
-
 
181
                data = buf;
-
 
182
 
-
 
183
                myInQueue.push(data);
-
 
184
 
-
 
185
                setEvent(ASYNCSOCKET_EVENT_DATA);
-
 
186
            }
223
            }
187
        }
224
        }
188
    }
225
    }
189
    catch (SocketException *e)
226
    catch (SocketException *e)
190
    {
227
    {
Line 229... Line 266...
229
        mySocket = -1;
266
        mySocket = -1;
230
    }
267
    }
231
   
268
   
232
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
269
    mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
233
 
270
 
234
    // TIME_WAIT - argh
-
 
235
    int on = 1;
271
    int on = 1;
236
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
272
    int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
237
    if (status == -1)
273
    if (status == -1)
238
    {
274
    {
-
 
275
        throw new SocketException("Connect:Reuseaddress: " + itos(errno));
-
 
276
    }
-
 
277
 
-
 
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
    {
239
        throw new SocketException("Connect: " + itos(errno));
289
        throw new SocketException("Connect:Keepidle: " + itos(errno));
240
    }
290
    }
241
 
291
 
242
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
292
    memset(&myAddressStruct, 0, sizeof(myAddressStruct));
243
 
293
 
244
    myAddressStruct.sin_family = AF_INET;
294
    myAddressStruct.sin_family = AF_INET;
Line 318... Line 368...
318
    fcntl(mySocket, F_SETOWN, getpid());
368
    fcntl(mySocket, F_SETOWN, getpid());
319
    int flags = fcntl(mySocket, F_GETFL);
369
    int flags = fcntl(mySocket, F_GETFL);
320
 
370
 
321
    if (flags < 0)
371
    if (flags < 0)
322
        throw new SocketException("Async socket fcntl failed");
372
        throw new SocketException("Async socket fcntl failed");
323
 
373
 
324
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
374
    fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
-
 
375
 
-
 
376
    myForceReconnect = false;
325
}
377
}
326
 
378
 
327
void AsyncSocket::close()
379
void AsyncSocket::close()
328
{
380
{
329
    if (mySocket != -1)
381
    if (mySocket != -1)
330
    {
382
    {
331
        ::close(mySocket);
383
        ::close(mySocket);
332
        mySocket = -1;
384
        mySocket = -1;
333
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
385
        setEvent(ASYNCSOCKET_EVENT_CLOSED);
Line 335... Line 387...
335
}
387
}
336
 
388
 
337
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
389
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
338
{
390
{
339
    myReconnectTimeout = timeout;
391
    myReconnectTimeout = timeout;
340
}
392
}
341
 
393
 
342
void AsyncSocket::startEvent()
394
void AsyncSocket::startEvent()
343
{
395
{
344
    myEventSemaphore.lock();
396
    myEventSemaphore.lock();
345
}
397
}
346
 
398
 
347
int AsyncSocket::getEvent()
399
int AsyncSocket::getEvent()
348
{
400
{
349
    int event = myEvent;
401
    int event = myEvent;
350
    myEvent = ASYNCSOCKET_EVENT_NONE;
402
    myEvent = ASYNCSOCKET_EVENT_NONE;
Line 354... Line 406...
354
void AsyncSocket::waitForEvent()
406
void AsyncSocket::waitForEvent()
355
{
407
{
356
    myEventSemaphore.wait();
408
    myEventSemaphore.wait();
357
}
409
}
358
 
410
 
359
void AsyncSocket::stopEvent()
411
void AsyncSocket::stopEvent()
360
{
412
{
361
    myEventSemaphore.unlock();
413
    myEventSemaphore.unlock();
362
}
414
}
363
 
415
 
364
void AsyncSocket::setAddress(string address, int port)
416
void AsyncSocket::setAddress(string address, int port)
365
{
417
{
366
    myAddress = address;
418
    myAddress = address;
367
    myPort = port;
419
    myPort = port;
368
}
420
}
369
 
421
 
370
bool AsyncSocket::availableData()
422
bool AsyncSocket::availableData()
371
{
423
{
372
    return (myInQueue.size() > 0);
424
    return (myInQueue.size() > 0);
373
}
425
}
374
 
426
 
375
string AsyncSocket::getData()
427
string AsyncSocket::getData()
376
{
428
{
Line 383... Line 435...
383
    mySemaphore.broadcast();
435
    mySemaphore.broadcast();
384
    return true;
436
    return true;
385
}
437
}
386
 
438
 
387
void AsyncSocket::setEvent(int event)
439
void AsyncSocket::setEvent(int event)
388
{
440
{
389
    myEventSemaphore.lock();
441
    myEventSemaphore.lock();
390
    myEvent = event;
442
    myEvent = event;
391
    myEventSemaphore.unlock();
443
    myEventSemaphore.unlock();
392
    myEventSemaphore.broadcast();
444
    myEventSemaphore.broadcast();
-
 
445
}
-
 
446
 
-
 
447
void AsyncSocket::forceReconnect()
-
 
448
{
-
 
449
    myForceReconnect = true;
-
 
450
    mySemaphore.broadcast();
393
}
451
}
394
 
452
 
395
void AsyncSocket::signalHandler(int signum)
453
void AsyncSocket::signalHandler(int signum)
396
{
454
{
-
 
455
    //FIXME: We must know which socket is ready to read by using select... 
397
    //cout << "DEBUG: signalHandler signum: " << signum << endl;
456
    //cout << "DEBUG: signalHandler signum: " << signum << endl;
398
    mySemaphore.broadcast();
457
    mySemaphore.broadcast();
399
}
458
}