diff --git a/src/util/tcpsocket.cpp b/src/util/tcpsocket.cpp
new file mode 100644
index 0000000..e6c17fd
--- /dev/null
+++ b/src/util/tcpsocket.cpp
@@ -0,0 +1,495 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see .
+ */
+#include
+#include
+#include
+#include //debug
+#include
+#include
+#include
+#include
+#include
+
+#include "tcpsocket.h"
+#include "misc.h"
+
+using namespace std;
+
+void CTcpData::SetData(uint8_t* data, int size, bool append)
+{
+ CopyData(reinterpret_cast(data), size, append);
+}
+
+/*void CTcpData::SetData(char* data, int size, bool append)
+{
+ CopyData(data, size, append);
+}
+
+void CTcpData::SetData(const uint8_t* data, int size, bool append)
+{
+ CopyData(reinterpret_cast(const_cast(data)), size, append);
+}
+
+void CTcpData::SetData(const char* data, int size, bool append)
+{
+ CopyData(const_cast(data), size, append);
+}*/
+
+void CTcpData::SetData(std::string data, bool append)
+{
+ CopyData(const_cast(data.c_str()), data.length(), append);
+}
+
+//store data as c-string
+void CTcpData::CopyData(char* data, int size, bool append)
+{
+ if (append)
+ {
+ int start = m_data.size() - 1;
+ m_data.resize(m_data.size() + size);
+ memcpy(&m_data[start], data, size);
+ m_data.back() = 0;
+ }
+ else
+ {
+ m_data.resize(size + 1);
+ memcpy(&m_data[0], data, size);
+ m_data.back() = 0;
+ }
+}
+
+void CTcpData::Clear()
+{
+ m_data.resize(1);
+ m_data.back() = 0;
+}
+
+CTcpSocket::CTcpSocket()
+{
+ m_sock = -1;
+ m_port = -1;
+}
+
+CTcpSocket::~CTcpSocket()
+{
+ Close();
+}
+
+//can't open int the base class
+int CTcpSocket::Open(std::string address, int port, int usectimeout)
+{
+ return FAIL;
+}
+
+void CTcpSocket::Close()
+{
+ if (m_sock != -1)
+ {
+ SetNonBlock(false);
+ close(m_sock);
+ m_sock = -1;
+ }
+}
+
+int CTcpSocket::SetNonBlock(bool nonblock)
+{
+ //non-blocking socket, because we work with select
+ int flags = fcntl(m_sock, F_GETFL);
+ if (flags == -1)
+ {
+ m_error = "F_GETFL " + GetErrno();
+ return FAIL;
+ }
+
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(m_sock, F_SETFL, flags) == -1)
+ {
+ m_error = "F_SETFL " + GetErrno();
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+int CTcpSocket::SetSockOptions()
+{
+ //set tcp keepalive
+ SetKeepalive();
+
+ //disable nagle algorithm
+ int flag = 1;
+ if (setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) == -1)
+ {
+ m_error = "TCP_NODELAY " + GetErrno();
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+int CTcpSocket::SetKeepalive()
+{
+#if defined(SO_KEEPALIVE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL)
+
+ int flag = 1;
+
+ //turn keepalive on
+ if (setsockopt(m_sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) == -1)
+ {
+ m_error = "SO_KEEPALIVE " + GetErrno();
+ return FAIL;
+ }
+
+ //two keepalive probes
+ flag = 2;
+ if (setsockopt(m_sock, IPPROTO_TCP, TCP_KEEPCNT, &flag, sizeof(flag)) == -1)
+ {
+ m_error = "TCP_KEEPCNT " + GetErrno();
+ return FAIL;
+ }
+
+ //20 seconds before we start using keepalive
+ flag = 20;
+ if (setsockopt(m_sock, IPPROTO_TCP, TCP_KEEPIDLE, &flag, sizeof(flag)) == -1)
+ {
+ m_error = "TCP_KEEPIDLE " + GetErrno();
+ return FAIL;
+ }
+
+ //20 seconds timeout of each keepalive packet
+ flag = 20;
+ if (setsockopt(m_sock, IPPROTO_TCP, TCP_KEEPINTVL, &flag, sizeof(flag)) == -1)
+ {
+ m_error = "TCP_KEEPINTVL " + GetErrno();
+ return FAIL;
+ }
+
+#else
+
+#warning keepalive support not compiled in
+
+#endif
+
+ return SUCCESS;
+}
+
+//wait until the socket becomes readable or writeable
+int CTcpSocket::WaitForSocket(bool write, std::string timeoutstr)
+{
+ int returnv;
+ fd_set rwsock;
+ struct timeval *tv = NULL;
+
+ //add the socket to the fd_set
+ FD_ZERO(&rwsock);
+ FD_SET(m_sock, &rwsock);
+
+ //set the timeout
+ struct timeval timeout;
+ if (m_usectimeout > 0)
+ {
+ timeout.tv_sec = m_usectimeout / 1000000;
+ timeout.tv_usec = m_usectimeout % 1000000;
+ tv = &timeout;
+ }
+
+ if (write)
+ returnv = select(m_sock + 1, NULL, &rwsock, NULL, tv);
+ else
+ returnv = select(m_sock + 1, &rwsock, NULL, NULL, tv);
+
+ if (returnv == 0) //select timed out
+ {
+ m_error = m_address + ":" + ToString(m_port) + " " + timeoutstr + " timed out";
+ return TIMEOUT;
+ }
+ else if (returnv == -1) //select had an error
+ {
+ m_error = "select() " + GetErrno();
+ return FAIL;
+ }
+
+ //check if the socket had any errors, connection refused is a common one
+ int sockstate, sockstatelen = sizeof(sockstate);
+ returnv = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &sockstate, reinterpret_cast(&sockstatelen));
+
+ if (returnv == -1) //getsockopt had an error
+ {
+ m_error = "getsockopt() " + GetErrno();
+ return FAIL;
+ }
+ else if (sockstate) //socket had an error
+ {
+ m_error = "SO_ERROR " + m_address + ":" + ToString(m_port) + " " + GetErrno(sockstate);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+//open a client socket
+int CTcpClientSocket::Open(std::string address, int port, int usectimeout /*= -1*/)
+{
+ Close(); //close it if it was opened
+
+ //store address, port and timeout
+ m_address = address;
+ m_port = port;
+ m_usectimeout = usectimeout;
+
+ m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (m_sock == -1) //can't make socket
+ {
+ m_error = "socket() " + GetErrno();
+ return FAIL;
+ }
+
+ if (SetNonBlock() != SUCCESS)
+ {
+ return FAIL;
+ }
+
+ struct sockaddr_in server; //where we connect to
+ memset(&server, 0, sizeof(server));
+
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = inet_addr(address.c_str());
+ server.sin_port = htons(port);
+
+ struct hostent *host = gethostbyname(address.c_str());
+ if (!host) //can't find host
+ {
+ m_error = "gethostbyname() " + address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+ server.sin_addr.s_addr = *reinterpret_cast(host->h_addr);
+
+ if (connect(m_sock, reinterpret_cast(&server), sizeof(server)) < 0)
+ {
+ if (errno != EINPROGRESS) //because of the non blocking socket, this means we're still connecting
+ {
+ m_error = "connect() " + address + ":" + ToString(port) + " " + GetErrno();
+ return FAIL;
+ }
+ }
+
+ int returnv = WaitForSocket(true, "Connect");//wait for the socket to become writeable, that means the connection is established
+
+ if (returnv == FAIL || returnv == TIMEOUT)
+ return returnv;
+
+ //if this fails the socket might still work, so don't return an error
+ SetSockOptions();
+
+ return SUCCESS;
+}
+
+int CTcpClientSocket::Read(CTcpData& data)
+{
+ uint8_t buff[1000];
+
+ if (m_sock == -1)
+ {
+ m_error = "socket closed";
+ return FAIL;
+ }
+
+ int returnv = WaitForSocket(false, "Read");//wait until the socket has something to read
+ if (returnv == FAIL || returnv == TIMEOUT)
+ return returnv;
+
+ //clear the tcpdata
+ data.Clear();
+
+ //loop until the socket has nothing more in its buffer
+ while(1)
+ {
+ int size = recv(m_sock, buff, sizeof(buff), 0);
+
+ if (errno == EAGAIN && size == -1) //we're done here, no more data, the call to WaitForSocket made sure there was at least some data to read
+ {
+ return SUCCESS;
+ }
+ else if (size == -1) //socket had an error
+ {
+ m_error = "recv() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+ else if (size == 0 && data.GetSize() == 0) //socket closed and no data received
+ {
+ m_error = m_address + ":" + ToString(m_port) + " Connection closed";
+ return FAIL;
+ }
+ else if (size == 0) //socket closed but data received
+ {
+ return SUCCESS;
+ }
+
+ data.SetData(buff, size, true); //append the data
+ }
+
+ return SUCCESS;
+}
+
+int CTcpClientSocket::Write(CTcpData& data)
+{
+ if (m_sock == -1)
+ {
+ m_error = "socket closed";
+ return FAIL;
+ }
+
+ int bytestowrite = data.GetSize();
+ int byteswritten = 0;
+
+ //loop until we've written all bytes
+ while (byteswritten < bytestowrite)
+ {
+ //wait until socket becomes writeable
+ int returnv = WaitForSocket(true, "Write");
+
+ if (returnv == FAIL || returnv == TIMEOUT)
+ return returnv;
+
+ int size = send(m_sock, data.GetData() + byteswritten, data.GetSize() - byteswritten, 0);
+
+ if (size == -1)
+ {
+ m_error = "send() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+
+ byteswritten += size;
+ }
+ return SUCCESS;
+}
+
+int CTcpClientSocket::SetInfo(std::string address, int port, int sock)
+{
+ m_address = address;
+ m_port = port;
+ m_sock = sock;
+
+ int returnv = SetNonBlock();
+ if (returnv == FAIL || returnv == TIMEOUT)
+ return returnv;
+
+ returnv = SetSockOptions();
+
+ return returnv;
+}
+
+int CTcpServerSocket::Open(std::string address, int port, int usectimeout)
+{
+ struct sockaddr_in bindaddr;
+
+ Close();
+
+ if (address.empty())
+ m_address = "*";
+ else
+ m_address = address;
+
+ m_port = port;
+ m_usectimeout = usectimeout;
+ m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (m_sock == -1)
+ {
+ m_error = "socket() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+
+ int opt = 1;
+ setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ //bind the socket
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ bindaddr.sin_family = AF_INET;
+ bindaddr.sin_port = htons(m_port);
+ if (address.empty())
+ {
+ bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else
+ {
+ struct hostent *host = gethostbyname(address.c_str());
+ if (host == NULL)
+ {
+ m_error = "gethostbyname() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+ bindaddr.sin_addr.s_addr = *reinterpret_cast(host->h_addr);
+ }
+
+ if (bind(m_sock, reinterpret_cast(&bindaddr), sizeof(bindaddr)) < 0)
+ {
+ m_error = "bind() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+
+ if (listen(m_sock, SOMAXCONN) < 0)
+ {
+ m_error = "listen() " + m_address + ":" + ToString(m_port) + " " + GetErrno();
+ return FAIL;
+ }
+
+ if (SetNonBlock() != SUCCESS)
+ {
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+int CTcpServerSocket::Accept(CTcpClientSocket& socket)
+{
+ struct sockaddr_in client;
+ socklen_t clientlen = sizeof(client);
+
+ if (m_sock == -1)
+ {
+ m_error = "socket closed";
+ return FAIL;
+ }
+
+ int returnv = WaitForSocket(false, "Accept"); //wait for socket to become readable
+ if (returnv == FAIL || returnv == TIMEOUT)
+ return returnv;
+
+ int sock = accept(m_sock, reinterpret_cast(&client), &clientlen);
+ if (sock < 0)
+ {
+ m_error = "accept() " + GetErrno();
+ return FAIL;
+ }
+
+ if (socket.SetInfo(inet_ntoa(client.sin_addr), ntohs(client.sin_port), sock) != SUCCESS)
+ {
+ m_error = socket.GetError();
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
diff --git a/src/util/tcpsocket.h b/src/util/tcpsocket.h
new file mode 100644
index 0000000..d8113dc
--- /dev/null
+++ b/src/util/tcpsocket.h
@@ -0,0 +1,95 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see .
+ */
+
+#ifndef TCP
+#define TCP
+
+#include
+#include
+#include
+
+#define FAIL 0
+#define SUCCESS 1
+#define TIMEOUT 2
+
+class CTcpData
+{
+ public:
+ void SetData(uint8_t* data, int size, bool append = false);
+ /*void SetData(char* data, int size, bool append = false);
+ void SetData(const uint8_t* data, int size, bool append = false);
+ void SetData(const char* data, int size, bool append = false);*/
+ void SetData(std::string data, bool append = false);
+
+ int GetSize() { return m_data.size() - 1; }
+ char* GetData() { return &m_data[0]; }
+
+ void Clear();
+
+ private:
+ std::vector m_data;
+ void CopyData(char* data, int size, bool append);
+};
+
+class CTcpSocket //base class
+{
+ public:
+ CTcpSocket();
+ ~CTcpSocket();
+
+ virtual int Open(std::string address, int port, int usectimeout = -1);
+ void Close();
+ bool IsOpen() { return m_sock != -1; }
+
+ std::string GetError() { return m_error; }
+ std::string GetAddress() { return m_address; }
+ int GetPort() { return m_port; }
+ int GetSock() { return m_sock; }
+
+ void SetTimeout(int usectimeout) { m_usectimeout = usectimeout; }
+
+ protected:
+ std::string m_address;
+ std::string m_error;
+
+ int m_sock;
+ int m_usectimeout;
+ int m_port;
+
+ int SetNonBlock(bool nonblock = true);
+ int SetSockOptions();
+ int SetKeepalive();
+ int WaitForSocket(bool write, std::string timeoutstr);
+};
+
+class CTcpClientSocket : public CTcpSocket
+{
+ public:
+ int Open(std::string address, int port, int usectimeout = -1);
+ int Read(CTcpData& data);
+ int Write(CTcpData& data);
+ int SetInfo(std::string address, int port, int sock);
+};
+
+class CTcpServerSocket : public CTcpSocket
+{
+ public:
+ int Open(std::string address, int port, int usectimeout = -1); //empty string for interface means bind to *
+ int Accept(CTcpClientSocket& socket);
+};
+#endif //TCP
diff --git a/wscript b/wscript
index e42c6bd..a448456 100644
--- a/wscript
+++ b/wscript
@@ -41,6 +41,7 @@ def build(bld):
src/util/mutex.cpp\
src/util/timeutils.cpp\
src/util/condition.cpp\
+ src/util/tcpsocket.cpp\
src/fft.cpp',
use=['m','pthread','rt', 'jack', 'fftw3', 'fftw3f', 'samplerate'],
includes='./src',