Subversion Repositories HomeAutomation

Rev

Rev 969 | Blame | Last modification | View Log | SVN | RSS feed

  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"
  23. #include <iostream>
  24. Semaphore AsyncSocket::mySemaphore;
  25.  
  26. AsyncSocket::AsyncSocket()
  27. {
  28.     mySocket = -1;
  29.     myReconnectTimeout = 10;
  30.     myForceReconnect = false;
  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.  
  50.     reconnectLoop();
  51.  
  52.     char buf[MAXBUFFER + 1];
  53.     string data;
  54.     int status;
  55.     int rc;
  56.     int timeSince = time(NULL) + 10;
  57.    
  58.     AsyncSocket::mySemaphore.lock();
  59.  
  60.     try
  61.     {
  62.         while (1)
  63.         {
  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.             }
  73.  
  74.             //cout << "Socket awoken...\n";
  75.            
  76.             if (rc == ETIMEDOUT)
  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.                 {
  88.                     data = myOutQueue.pop();
  89.  
  90.                     //slog << "Sending: " << data << "\n";
  91.  
  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";
  161.  
  162.                 if (status == -1)
  163.                 {
  164.                     switch (errno)
  165.                     {
  166.                         case EAGAIN:
  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";
  168.                         break;
  169.  
  170.                         case EBADF:
  171.                         throw new SocketException("The argument s is an invalid descriptor.");
  172.  
  173.                         case ECONNREFUSED:
  174.                         throw new SocketException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
  175.  
  176.                         case EFAULT:
  177.                         throw new SocketException("The receive buffer pointer(s) point outside the process's address space.");
  178.  
  179.                         case EINTR:
  180.                         throw new SocketException("The receive was interrupted by delivery of a signal before any data were available; see signal(7).");
  181.  
  182.                         case EINVAL:
  183.                         //throw new SocketException("2Invalid argument passed.");
  184.                         slog << "Disconnected from server.\n";
  185.                         reconnectLoop();
  186.                         break;
  187.  
  188.                         case ENOMEM:
  189.                         throw new SocketException("Could not allocate memory for recvmsg().");
  190.  
  191.                         case ENOTCONN:
  192.                         throw new SocketException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and  accept(2)).");
  193.  
  194.                         case ENOTSOCK:
  195.                         throw new SocketException("The argument s does not refer to a socket.");
  196.  
  197.                         default:
  198.                         slog << "Unknow exception: " << errno << "\n";
  199.                         break;
  200.                     }
  201.                 }
  202.                 else if (status == 0)
  203.                 {
  204.                     slog << "Disconnected from server.\n";
  205.                     reconnectLoop();
  206.                 }
  207.                 else if (status > 0)
  208.                 {
  209.                     data = buf;
  210.  
  211.                     myInQueue.push(data);
  212.  
  213.                     setEvent(ASYNCSOCKET_EVENT_DATA);
  214.                    
  215.                     timeSince = time(NULL);
  216.                 }
  217.                
  218.                 if (timeSince + 10 < time(NULL))
  219.                 {
  220.                     setEvent(ASYNCSOCKET_EVENT_INACTIVITY);
  221.                     timeSince = time(NULL);
  222.                 }
  223.             }
  224.         }
  225.     }
  226.     catch (SocketException *e)
  227.     {
  228.         slog << "Exception: " << e->getDescription() << "\n";
  229.         mySemaphore.unlock();
  230.         setEvent(ASYNCSOCKET_EVENT_DIED);
  231.         stop();
  232.     }
  233.  
  234.     close();
  235.  
  236.     mySemaphore.unlock();
  237. }
  238.  
  239. void AsyncSocket::reconnectLoop()
  240. {
  241.     SyslogStream &slog = SyslogStream::getInstance();
  242.  
  243.     while (1)
  244.     {
  245.         try
  246.         {
  247.             slog << "Trying to connect...\n";
  248.             connect();
  249.             slog << "Connection established.\n";
  250.             break;
  251.         }
  252.         catch (SocketException *e)
  253.         {
  254.             slog << "Could not connect: " << e->getDescription() << "\n";
  255.             slog << "Will try again in " << myReconnectTimeout << " seconds\n";
  256.             sleep(myReconnectTimeout);
  257.         }
  258.     }
  259. }
  260.  
  261. void AsyncSocket::connect()
  262. {
  263.     if (mySocket != -1)
  264.     {
  265.         ::close(mySocket);
  266.         mySocket = -1;
  267.     }
  268.    
  269.     mySocket = ::socket(AF_INET, SOCK_STREAM, 0);
  270.  
  271.     int on = 1;
  272.     int status = setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
  273.     if (status == -1)
  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.     {
  289.         throw new SocketException("Connect:Keepidle: " + itos(errno));
  290.     }
  291.  
  292.     memset(&myAddressStruct, 0, sizeof(myAddressStruct));
  293.  
  294.     myAddressStruct.sin_family = AF_INET;
  295.     myAddressStruct.sin_port = htons(myPort);
  296.  
  297.     status = inet_pton(AF_INET, myAddress.c_str(), &myAddressStruct.sin_addr);
  298.  
  299.     if (status == -1)
  300.     {
  301.         if (errno == EAFNOSUPPORT)
  302.             throw new SocketException("Connect: EAFNOSUPPORT");
  303.     }
  304.    
  305.     status = ::connect(mySocket, (sockaddr*)&myAddressStruct, sizeof(myAddressStruct));
  306.  
  307.     if (status == -1)
  308.     {
  309.         switch (errno)
  310.         {
  311.             case EACCES:
  312.             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).)");
  313.  
  314.             case EPERM:
  315.             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.");
  316.  
  317.             case EADDRINUSE:
  318.             throw new SocketException("Local address is already in use.");
  319.  
  320.             case EAFNOSUPPORT:
  321.             throw new SocketException("The passed address didn't have the correct address family in its sa_family field.");
  322.  
  323.             case EAGAIN:
  324.             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.");
  325.  
  326.             case EALREADY:
  327.             throw new SocketException("The socket is non-blocking and a previous connection attempt has not yet been completed.");
  328.  
  329.             case EBADF:
  330.             throw new SocketException("The file descriptor is not a valid index in the descriptor table.");
  331.  
  332.             case ECONNREFUSED:
  333.             throw new SocketException("No-one listening on the remote address.");
  334.  
  335.             case EFAULT:
  336.             throw new SocketException("The socket structure address is outside the user's address space.");
  337.  
  338.             case EINPROGRESS:
  339.             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).");
  340.  
  341.             case EINTR:
  342.             throw new SocketException("The system call was interrupted by a signal that was caught; see signal(7).");
  343.  
  344.             case EISCONN:
  345.             throw new SocketException("The socket is already connected.");
  346.  
  347.             case ENETUNREACH:
  348.             throw new SocketException("Network is unreachable.");
  349.  
  350.             case ENOTSOCK:
  351.             throw new SocketException("The file descriptor is not associated with a socket.");
  352.  
  353.             case ETIMEDOUT:
  354.             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.");
  355.  
  356.             default:
  357.             throw new SocketException("Other errors may be generated by the underlying protocol modules. : " + itos(errno));
  358.         }
  359.     }
  360.  
  361.     struct sigaction saio;
  362.     saio.sa_handler = AsyncSocket::signalHandler;
  363.     sigemptyset(&saio.sa_mask);
  364.     saio.sa_flags = 0;
  365.     saio.sa_restorer = NULL;
  366.     sigaction(SIGIO, &saio, NULL);
  367.  
  368.     fcntl(mySocket, F_SETOWN, getpid());
  369.     int flags = fcntl(mySocket, F_GETFL);
  370.  
  371.     if (flags < 0)
  372.         throw new SocketException("Async socket fcntl failed");
  373.  
  374.     fcntl(mySocket, F_SETFL, flags | O_NONBLOCK | FASYNC);
  375.  
  376.     myForceReconnect = false;
  377. }
  378.  
  379. void AsyncSocket::close()
  380. {
  381.     if (mySocket != -1)
  382.     {
  383.         ::close(mySocket);
  384.         mySocket = -1;
  385.         setEvent(ASYNCSOCKET_EVENT_CLOSED);
  386.     }
  387. }
  388.  
  389. void AsyncSocket::setReconnectTimeout(unsigned int timeout)
  390. {
  391.     myReconnectTimeout = timeout;
  392. }
  393.  
  394. void AsyncSocket::startEvent()
  395. {
  396.     myEventSemaphore.lock();
  397. }
  398.  
  399. int AsyncSocket::getEvent()
  400. {
  401.     int event = myEvent;
  402.     myEvent = ASYNCSOCKET_EVENT_NONE;
  403.     return event;
  404. }
  405.  
  406. void AsyncSocket::waitForEvent()
  407. {
  408.     myEventSemaphore.wait();
  409. }
  410.  
  411. void AsyncSocket::stopEvent()
  412. {
  413.     myEventSemaphore.unlock();
  414. }
  415.  
  416. void AsyncSocket::setAddress(string address, int port)
  417. {
  418.     myAddress = address;
  419.     myPort = port;
  420. }
  421.  
  422. bool AsyncSocket::availableData()
  423. {
  424.     return (myInQueue.size() > 0);
  425. }
  426.  
  427. string AsyncSocket::getData()
  428. {
  429.     return myInQueue.pop();
  430. }
  431.  
  432. bool AsyncSocket::sendData(string data)
  433. {
  434.     myOutQueue.push(data);
  435.     mySemaphore.broadcast();
  436.     return true;
  437. }
  438.  
  439. void AsyncSocket::setEvent(int event)
  440. {
  441.     myEventSemaphore.lock();
  442.     myEvent = event;
  443.     myEventSemaphore.unlock();
  444.     myEventSemaphore.broadcast();
  445. }
  446.  
  447. void AsyncSocket::forceReconnect()
  448. {
  449.     myForceReconnect = true;
  450.     mySemaphore.broadcast();
  451. }
  452.  
  453. void AsyncSocket::signalHandler(int signum)
  454. {
  455.     //FIXME: We must know which socket is ready to read by using select...
  456.     //cout << "DEBUG: signalHandler signum: " << signum << endl;
  457.     mySemaphore.broadcast();
  458. }
  459.