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