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
}