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
 
969 runge 24
Semaphore AsyncSocket::mySemaphore;
25
 
26
AsyncSocket::AsyncSocket()
27
{
28
	mySocket = -1;
976 runge 29
	myReconnectTimeout = 0;
974 runge 30
	myForceReconnect = false;
969 runge 31
 
32
	Thread<AsyncSocket>();
33
}
34
 
35
AsyncSocket::~AsyncSocket()
36
{
37
	if (mySocket != -1)
38
	{
39
		::close(mySocket);
40
		mySocket = -1;
41
	}
42
 
43
	stop();
44
}
45
 
46
void AsyncSocket::run()
47
{
48
	SyslogStream &slog = SyslogStream::getInstance();
49
 
976 runge 50
	if (myReconnectTimeout == 0)
51
	{
52
		connect();
53
	}
54
	else
55
	{
56
		reconnectLoop();
57
	}
969 runge 58
 
59
	char buf[MAXBUFFER + 1];
60
	string data;
61
	int status;
974 runge 62
	int rc;
63
	int timeSince = time(NULL) + 10;
64
 
976 runge 65
 
969 runge 66
 
67
	try
68
	{
69
		while (1)
70
		{
976 runge 71
			AsyncSocket::mySemaphore.lock();
969 runge 72
 
976 runge 73
			if (myReconnectTimeout == 0)
74
			{
75
				rc = mySemaphore.wait();
76
			}
77
			else
78
			{
79
				rc = mySemaphore.wait(10);
80
			}
81
 
82
			AsyncSocket::mySemaphore.unlock();
83
 
974 runge 84
			if (myForceReconnect)
85
			{
86
				slog << "Disconnected from server.\n";
87
				reconnectLoop();
88
				timeSince = time(NULL) + 10;
89
				continue;
90
			}
91
 
969 runge 92
			//cout << "Socket awoken...\n";
974 runge 93
 
94
			if (rc == ETIMEDOUT)
95
			{
96
				/* Socket timed out, this means we have not received anything in some time
97
				and we should check the connection */
969 runge 98
 
974 runge 99
				setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
100
				timeSince = time(NULL);
101
			}
102
			else
969 runge 103
			{
974 runge 104
				while (myOutQueue.size() > 0)
105
				{
106
					data = myOutQueue.pop();
969 runge 107
 
974 runge 108
					//slog << "Sending: " << data << "\n";
969 runge 109
 
974 runge 110
					status = ::send(mySocket, data.c_str(), data.size(), 0);
969 runge 111
 
974 runge 112
					//slog << "Status: " << status << "\n";
113
 
114
					if (status == -1)
969 runge 115
					{
974 runge 116
						switch (errno)
117
						{
118
							case EACCES:
119
							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).)");
969 runge 120
 
974 runge 121
							case EAGAIN:
122
							//slog << "The socket is marked non-blocking and the requested operation would block.\n";
123
							break;
969 runge 124
 
974 runge 125
							case EBADF:
126
							throw new SocketException("An invalid descriptor was specified.");
969 runge 127
 
974 runge 128
							case ECONNRESET:
129
							throw new SocketException("Connection reset by peer.");
969 runge 130
 
974 runge 131
							case EDESTADDRREQ:
132
							throw new SocketException("The socket is not connection-mode, and no peer address is set.");
969 runge 133
 
974 runge 134
							case EFAULT:
135
							throw new SocketException("An invalid user space address was specified for an argument.");
969 runge 136
 
974 runge 137
							case EINTR:
138
							throw new SocketException("A signal occurred before any data was transmitted; see signal(7).");
969 runge 139
 
974 runge 140
							case EINVAL:
141
							throw new SocketException("1Invalid argument passed.");
969 runge 142
 
974 runge 143
							case EISCONN:
144
							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.)");
969 runge 145
 
974 runge 146
							case EMSGSIZE:
147
							throw new SocketException("The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.");
969 runge 148
 
974 runge 149
							case ENOBUFS:
150
							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.)");
969 runge 151
 
974 runge 152
							case ENOMEM:
153
							throw new SocketException("No memory available.");
969 runge 154
 
974 runge 155
							case ENOTCONN:
156
							throw new SocketException("The socket is not connected, and no target has been given.");
969 runge 157
 
974 runge 158
							case ENOTSOCK:
159
							throw new SocketException("The argument s is not a socket.");
969 runge 160
 
974 runge 161
							case EOPNOTSUPP:
162
							throw new SocketException("Some bit in the flags argument is inappropriate for the socket type.");
969 runge 163
 
974 runge 164
							case EPIPE:
165
							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.");
969 runge 166
 
974 runge 167
							default:
976 runge 168
							slog << "Unknow exception: " + itos(errno) + "\n";
974 runge 169
							break;
170
						}
969 runge 171
					}
172
				}
173
 
974 runge 174
				memset(buf, 0, MAXBUFFER + 1);
969 runge 175
 
974 runge 176
				status = ::recv(mySocket, buf, MAXBUFFER, 0);
969 runge 177
 
974 runge 178
				if (status == -1)
969 runge 179
				{
974 runge 180
					switch (errno)
181
					{
182
						case EAGAIN:
183
						//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";
184
						break;
969 runge 185
 
974 runge 186
						case EBADF:
187
						throw new SocketException("The argument s is an invalid descriptor.");
969 runge 188
 
974 runge 189
						case ECONNREFUSED:
190
						throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
969 runge 191
 
974 runge 192
						case EFAULT:
193
						throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
969 runge 194
 
974 runge 195
						case EINTR:
196
						throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
969 runge 197
 
974 runge 198
						case EINVAL:
199
						//throw new SocketException("2Invalid argument passed.");
200
						slog << "Disconnected from server.\n";
201
						reconnectLoop();
976 runge 202
						timeSince = time(NULL) + 10;
974 runge 203
						break;
969 runge 204
 
974 runge 205
						case ENOMEM:
206
						throw new SocketException("Could not allocate memory for recvmsg().");
969 runge 207
 
974 runge 208
						case ENOTCONN:
209
						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 210
 
974 runge 211
						case ENOTSOCK:
212
						throw new SocketException("The argument s does not refer to a socket.");
969 runge 213
 
974 runge 214
						default:
976 runge 215
						slog << "Unknow exception: " + itos(errno) + "\n";
974 runge 216
						break;
217
					}
969 runge 218
				}
974 runge 219
				else if (status == 0)
220
				{
221
					slog << "Disconnected from server.\n";
222
					reconnectLoop();
976 runge 223
					timeSince = time(NULL);
974 runge 224
				}
225
				else if (status > 0)
226
				{
976 runge 227
					timeSince = time(NULL) + 10;
228
 
974 runge 229
					data = buf;
969 runge 230
 
976 runge 231
					//slog << "Receiving: " + data + "\n";
232
 
974 runge 233
					myInQueue.push(data);
969 runge 234
 
974 runge 235
					setEvent(ASYNCSOCKET_EVENT_DATA);
236
				}
237
 
238
				if (timeSince + 10 < time(NULL))
239
				{
240
					setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
241
					timeSince = time(NULL);
242
				}
969 runge 243
			}
244
		}
245
	}
246
	catch (SocketException *e)
247
	{
976 runge 248
		slog << "Exception: " + e->getDescription() + "\n";
249
		//mySemaphore.unlock();
969 runge 250
		setEvent(ASYNCSOCKET_EVENT_DIED);
251
		stop();
252
	}
253
 
254
	close();
255
 
976 runge 256
	//mySemaphore.unlock();
969 runge 257
}
258
 
259
void AsyncSocket::reconnectLoop()
260
{
976 runge 261
	if (myReconnectTimeout == 0)
262
	{
263
		throw new SocketException("Connection is closed.");
264
	}
265
 
969 runge 266
	SyslogStream &slog = SyslogStream::getInstance();
267
 
268
	while (1)
269
	{
270
		try
271
		{
272
			connect();
273
			break;
274
		}
275
		catch (SocketException *e)
276
		{
976 runge 277
			slog << "Could not connect: " + e->getDescription() + "\n";
278
			slog << "Will try again in " + itos(myReconnectTimeout) + " seconds\n";
969 runge 279
			sleep(myReconnectTimeout);
280
		}
281
	}
282
}
283
 
284
void AsyncSocket::connect()
285
{
976 runge 286
	SyslogStream &slog = SyslogStream::getInstance();
287
 
975 runge 288
	close();
976 runge 289
 
290
	slog << "Trying to connect...\n";
291
 
969 runge 292
	mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
293
 
294
	int on = 1;
295
	int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
296
	if (status == -1)
297
	{
974 runge 298
		throw new SocketException("Connect:Reuseaddress: " + itos(errno));
969 runge 299
	}
300
 
974 runge 301
	///FIXME: Verify that this works
302
	status = setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on));
303
	if (status == -1)
304
	{
305
		throw new SocketException("Connect:Keepalive: " + itos(errno));
306
	}
307
 
308
	///FIXME: Verify that this works
309
	status = setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, (const char*)&on, sizeof(on));
310
	if (status == -1)
311
	{
312
		throw new SocketException("Connect:Keepidle: " + itos(errno));
313
	}
314
 
969 runge 315
	memset(&myAddressStruct, 0, sizeof(myAddressStruct));
316
 
317
	myAddressStruct.sin_family = AF_INET;
318
	myAddressStruct.sin_port = htons(myPort);
319
 
976 runge 320
	struct hostent *hptr = gethostbyname(myAddress.c_str());
321
	if (hptr == NULL)
322
	{
323
		throw new SocketException("Connect: Could not resolv ip address");
324
	}
325
 
326
	memcpy(&myAddressStruct.sin_addr, hptr->h_addr, hptr->h_length);
327
	/*
969 runge 328
	status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
329
 
330
	if (status == -1)
331
	{
332
		if (errno == EAFNOSUPPORT)
333
			throw new SocketException("Connect: EAFNOSUPPORT");
334
	}
976 runge 335
	*/
336
 
969 runge 337
 
338
	status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
339
 
340
	if (status == -1)
341
	{
342
		switch (errno)
343
		{
344
			case EACCES:
345
			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).)");
346
 
347
			case EPERM:
348
			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.");
349
 
350
			case EADDRINUSE:
351
			throw new SocketException("Local address is already in use.");
352
 
353
			case EAFNOSUPPORT:
354
			throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
355
 
356
			case EAGAIN:
357
			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.");
358
 
359
			case EALREADY:
360
			throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
361
 
362
			case EBADF:
363
			throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
364
 
365
			case ECONNREFUSED:
366
			throw new SocketException("No-one listening on the remote address.");
367
 
368
			case EFAULT:
369
			throw new SocketException("The socket structure address is outside the user's address space.");
370
 
371
			case EINPROGRESS:
372
			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).");
373
 
374
			case EINTR:
375
			throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
376
 
377
			case EISCONN:
378
			throw new SocketException("The socket is already connected.");
379
 
380
			case ENETUNREACH:
381
			throw new SocketException("Network is unreachable.");
382
 
383
			case ENOTSOCK:
384
			throw new SocketException("The file descriptor is not associated with a socket.");
385
 
386
			case ETIMEDOUT:
387
			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.");
388
 
389
			default:
390
			throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
391
		}
392
	}
393
 
394
	struct sigaction saio;
395
	saio.sa_handler = AsyncSocket::signalHandler;
396
	sigemptyset(&saio.sa_mask);
397
	saio.sa_flags = 0;
398
	saio.sa_restorer = NULL;
399
	sigaction(SIGIO, &saio, NULL);
400
 
401
	fcntl(mySocket, F_SETOWN, getpid());
402
	int flags = fcntl(mySocket, F_GETFL);
403
 
404
	if (flags < 0)
405
		throw new SocketException("Async socket fcntl failed");
406
 
407
	fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
974 runge 408
 
409
	myForceReconnect = false;
976 runge 410
 
411
	slog << "Connection established.\n";
969 runge 412
}
413
 
414
void AsyncSocket::close()
415
{
416
	if (mySocket != -1)
417
	{
418
		::close(mySocket);
419
		mySocket = -1;
420
		setEvent(ASYNCSOCKET_EVENT_CLOSED);
421
	}
422
}
423
 
424
void AsyncSocket::setReconnectTimeout(unsigned int timeout)
425
{
426
	myReconnectTimeout = timeout;
427
}
428
 
429
void AsyncSocket::startEvent()
430
{
431
	myEventSemaphore.lock();
432
}
433
 
434
int AsyncSocket::getEvent()
435
{
436
	int event = myEvent;
437
	myEvent = ASYNCSOCKET_EVENT_NONE;
438
	return event;
439
}
440
 
441
void AsyncSocket::waitForEvent()
442
{
443
	myEventSemaphore.wait();
444
}
445
 
446
void AsyncSocket::stopEvent()
447
{
448
	myEventSemaphore.unlock();
449
}
450
 
451
void AsyncSocket::setAddress(string address, int port)
452
{
453
	myAddress = address;
454
	myPort = port;
455
}
456
 
457
bool AsyncSocket::availableData()
458
{
459
	return (myInQueue.size() > 0);
460
}
461
 
462
string AsyncSocket::getData()
463
{
464
	return myInQueue.pop();
465
}
466
 
467
bool AsyncSocket::sendData(string data)
468
{
469
	myOutQueue.push(data);
470
	mySemaphore.broadcast();
471
	return true;
472
}
473
 
474
void AsyncSocket::setEvent(int event)
475
{
476
	myEventSemaphore.lock();
477
	myEvent = event;
478
	myEventSemaphore.unlock();
479
	myEventSemaphore.broadcast();
480
}
481
 
974 runge 482
void AsyncSocket::forceReconnect()
483
{
484
	myForceReconnect = true;
485
	mySemaphore.broadcast();
486
}
487
 
969 runge 488
void AsyncSocket::signalHandler(int signum)
489
{
974 runge 490
	//FIXME: We must know which socket is ready to read by using select... 
969 runge 491
	//cout << "DEBUG: signalHandler signum: " << signum << endl;
492
	mySemaphore.broadcast();
493
}