Logo Search packages:      
Sourcecode: qmidinet version File versions  Download package

qmidinetUdpDevice.cpp

// qmidinetUdpDevice.cpp
//
/****************************************************************************
   Copyright (C) 2010, rncbc aka Rui Nuno Capela. All rights reserved.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*****************************************************************************/

#include "qmidinetAbout.h"
#include "qmidinetUdpDevice.h"

#include <stdlib.h>
#include <string.h>

#if defined(WIN32)
static WSADATA g_wsaData;
typedef int socklen_t;
#else
#include <unistd.h>
#include <sys/ioctl.h>
inline void closesocket(int s) { ::close(s); }
#endif

#include <QByteArray>
#include <QThread>


//----------------------------------------------------------------------------
// qmidinetUdpDevice::RecvThread -- Network listener thread.
//

class qmidinetUdpDeviceThread : public QThread
{
public:

      // Constructor.
      qmidinetUdpDeviceThread(int *sockin, int nports = 1);

      // Run-state accessors.
      void setRunState(bool bRunState);
      bool runState() const;

protected:

      // The main thread executive.
      void run();

private:

      // The listener socket.
      int *m_sockin;
      int  m_nports;

      // Whether the thread is logically running.
      volatile bool m_bRunState;
};


// Constructor.
qmidinetUdpDeviceThread::qmidinetUdpDeviceThread ( int *sockin, int nports )
      : QThread(), m_sockin(sockin), m_nports(nports), m_bRunState(false)
{
}


// Run-state accessors.
void qmidinetUdpDeviceThread::setRunState ( bool bRunState )
{
      m_bRunState = bRunState;
}

bool qmidinetUdpDeviceThread::runState (void) const
{
      return m_bRunState;
}


// The main thread executive.
void qmidinetUdpDeviceThread::run (void)
{
      m_bRunState = true;

      while (m_bRunState) {

            // Wait for an network event...
            fd_set fds;
            FD_ZERO(&fds);

            int i, fdmax = 0;
            for(i = 0; i < m_nports; ++i) {
                  FD_SET(m_sockin[i], &fds);
                  if (m_sockin[i] > fdmax)
                        fdmax = m_sockin[i];
            }

            // Set timeout period (1 second)...
            struct timeval tv;
            tv.tv_sec  = 1;
            tv.tv_usec = 0;

            int s = ::select(fdmax + 1, &fds, NULL, NULL, &tv);
            if (s < 0) {
                  ::perror("select");
                  break;
            }
            if (s == 0) {
                  // Timeout!
                  continue;
            }

            // A Network event
            for (i = 0; i < m_nports; ++i) {
                  if (FD_ISSET(m_sockin[i], &fds)) {
                        // Read from network...
                        unsigned char buf[1024];
                        struct sockaddr_in sender;
                        socklen_t slen = sizeof(sender);
                        int r = ::recvfrom(m_sockin[i], (char *) buf, sizeof(buf),
                              0, (struct sockaddr *) &sender, &slen);
                        if (r > 0)
                              qmidinetUdpDevice::getInstance()->recvData(buf, r, i);
                        else
                        if (r < 0)
                              ::perror("recvfrom");
                  }
            }
      }
}


//----------------------------------------------------------------------------
// qmidinetUdpDevice -- Network interface device (UDP/IP).
//

qmidinetUdpDevice *qmidinetUdpDevice::g_pDevice = NULL;

// Constructor.
qmidinetUdpDevice::qmidinetUdpDevice ( QObject *pParent )
      : QObject(pParent), m_nports(0),
            m_sockin(NULL), m_sockout(NULL), m_addrout(NULL), m_pRecvThread(NULL)
{
#if defined(WIN32)
      WSAStartup(MAKEWORD(1, 1), &g_wsaData);
#endif

      g_pDevice = this;
}

// Destructor.
qmidinetUdpDevice::~qmidinetUdpDevice (void)
{
      close();

      g_pDevice = NULL;

#if defined(WIN32)
      WSACleanup();
#endif
}


// Kind of singleton reference.
qmidinetUdpDevice *qmidinetUdpDevice::getInstance (void)
{
      return g_pDevice;
}


// Device initialization method.
bool qmidinetUdpDevice::open (
      const QString& sInterface, int iUdpPort, int iNumPorts )
{
      // Close if already open.
      close();

      // Setup network protocol...
      int i, protonum = 0;
#if 0
      struct protoent *proto = ::getprotobyname("IP");
      if (proto)
            protonum = proto->p_proto;
#endif

      // Stable interface name...
      const char *ifname = NULL;
      const QByteArray aInterface = sInterface.toLocal8Bit();
      if (!aInterface.isEmpty())
            ifname = aInterface.constData();

      // Set the number of ports.
      m_nports = iNumPorts;

      // Input socket stuff...
      //
      m_sockin = new int [m_nports];
      for (i = 0; i < m_nports; ++i)
            m_sockin[i] = -1;

      for (i = 0; i < m_nports; ++i) {

            m_sockin[i] = ::socket(PF_INET, SOCK_DGRAM, protonum);
            if (m_sockin[i] < 0) {
                  ::perror("socket(in)");
                  return false;
            }

            struct sockaddr_in addrin;
            ::memset(&addrin, 0, sizeof(addrin));
            addrin.sin_family = AF_INET;
            addrin.sin_addr.s_addr = htonl(INADDR_ANY);
            addrin.sin_port = htons(iUdpPort + i);

            if (::bind(m_sockin[i], (struct sockaddr *) (&addrin), sizeof(addrin)) < 0) {
                  ::perror("bind");
                  return false;
            }

            // Will Hall, 2007
            // INADDR_ANY will bind to default interface,
            // specify alternate interface nameon which to bind...
            struct in_addr if_addr_in;
            if (ifname) {
                  if (!get_address(m_sockin[i], &if_addr_in, ifname)) {
                        fprintf(stderr, "socket(in): could not find interface address for %s\n", ifname);
                        return false;
                  }
                  if (::setsockopt(m_sockin[i], IPPROTO_IP, IP_MULTICAST_IF,
                              (char *) &if_addr_in, sizeof(if_addr_in))) {
                        ::perror("setsockopt(IP_MULTICAST_IF)");
                        return false;
                  }
            } else {
                  if_addr_in.s_addr = htonl(INADDR_ANY);
            }

            struct ip_mreq mreq;
            mreq.imr_multiaddr.s_addr = ::inet_addr("225.0.0.37");
            mreq.imr_interface.s_addr = if_addr_in.s_addr;
            if(::setsockopt (m_sockin[i], IPPROTO_IP, IP_ADD_MEMBERSHIP,
                        (char *) &mreq, sizeof(mreq)) < 0) {
                  ::perror("setsockopt(IP_ADD_MEMBERSHIP)");
                  fprintf(stderr, "socket(in): your kernel is probably missing multicast support.\n");
                  return false;
            }
      }

      // Output socket...
      //
      m_sockout = new int [m_nports];
      m_addrout = new struct sockaddr_in [m_nports];

      for (i = 0; i < m_nports; ++i)
            m_sockout[i] = -1;

      for (i = 0; i < m_nports; ++i) {

            m_sockout[i] = ::socket(AF_INET, SOCK_DGRAM, protonum);
            if (m_sockout[i] < 0) {
                  ::perror("socket(out)");
                  return false;
            }

            // Will Hall, Oct 2007
            if (ifname) {
                  struct in_addr if_addr_out;
                  if (!get_address(m_sockout[i], &if_addr_out, ifname)) {
                        ::fprintf(stderr, "socket(out): could not find interface address for %s\n", ifname);
                        return false;
                  }
                  if (::setsockopt(m_sockout[i], IPPROTO_IP, IP_MULTICAST_IF,
                              (char *) &if_addr_out, sizeof(if_addr_out))) {
                        ::perror("setsockopt(IP_MULTICAST_IF)");
                        return false;
                  }
            }

            ::memset(&m_addrout[i], 0, sizeof(struct sockaddr_in));
            m_addrout[i].sin_family = AF_INET;
            m_addrout[i].sin_addr.s_addr = ::inet_addr("225.0.0.37");
            m_addrout[i].sin_port = htons(iUdpPort + i);

            // Turn off loopback...
            int loop = 0;
            if (::setsockopt(m_sockout[i], IPPROTO_IP, IP_MULTICAST_LOOP,
                        (char *) &loop, sizeof (loop)) < 0) {
                  ::perror("setsockopt(IP_MULTICAST_LOOP)");
                  return false;
            }
      }

      // Start listener thread...
      m_pRecvThread = new qmidinetUdpDeviceThread(m_sockin, m_nports);
      m_pRecvThread->start();

      // Done.
      return true;
}


// Device termination method.
void qmidinetUdpDevice::close (void)
{
      if (m_sockin) {
            for (int i = 0; i < m_nports; ++i) {
                  if (m_sockin[i] >= 0)
                        ::closesocket(m_sockin[i]);
            }
            delete [] m_sockin;
            m_sockin = NULL;
      }

      if (m_sockout) {
            for (int i = 0; i < m_nports; ++i) {
                  if (m_sockout[i] >= 0)
                        ::closesocket(m_sockout[i]);
            }
            delete [] m_sockout;
            m_sockout = NULL;
      }

      if (m_addrout) {
            delete [] m_addrout;
            m_addrout = NULL;
      }

      if (m_pRecvThread) {
            if (m_pRecvThread->isRunning()) {
                  m_pRecvThread->setRunState(false);
            //    m_pRecvThread->terminate();
                  m_pRecvThread->wait(1200); // Timeout>1sec.
            }
            delete m_pRecvThread;
            m_pRecvThread = NULL;
      }

      m_nports = 0;
}


// Data transmission methods.
bool qmidinetUdpDevice::sendData (
      unsigned char *data, unsigned short len, int port ) const
{
      if (port < 0 || port >= m_nports)
            return false;

      if (m_sockout == NULL)
            return false;
      if (m_sockout[port] < 0)
            return false;

      if (::sendto(m_sockout[port], (char *) data, len, 0,
                  (struct sockaddr *) &m_addrout[port],
                  sizeof(struct sockaddr_in)) < 0) {
            ::perror("sendto");
            return false;
      }

      return true;
}


void qmidinetUdpDevice::recvData (
      unsigned char *data, unsigned short len, int port )
{
      emit received(QByteArray((const char *) data, len), port);
}


// Receive data slot.
void qmidinetUdpDevice::receive ( const QByteArray& data, int port )
{
      sendData((unsigned char *) data.constData(), data.length(), port);
}


// Get interface address from supplied name.
bool qmidinetUdpDevice::get_address (
      int sock, struct in_addr *inaddr, const char *ifname )
{
#if !defined(WIN32)

      struct ifreq ifr;
      ::strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));

      if (::ioctl(sock, SIOCGIFFLAGS, (char *) &ifr)) {
            ::perror("ioctl(SIOCGIFFLAGS)");
            return false;
      }

      if ((ifr.ifr_flags & IFF_UP) == 0) {
            fprintf(stderr, "interface %s is down\n", ifname);
            return false;
      }

      if (::ioctl(sock, SIOCGIFADDR, (char *) &ifr)) {
            ::perror("ioctl(SIOCGIFADDR)");
            return false;
      }

      struct sockaddr_in sa;
      ::memcpy(&sa, &ifr.ifr_addr, sizeof(struct sockaddr_in));
      inaddr->s_addr = sa.sin_addr.s_addr;

      return true;

#else

      return false;

#endif      // !WIN32
}


// end of qmidinetUdpDevice.h

Generated by  Doxygen 1.6.0   Back to index