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