/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org DESCRIPTION: Abstraction for sockets. */ /* This file is part of ODR-DabMod. ODR-DabMod 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. ODR-DabMod 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 ODR-DabMod. If not, see . */ #pragma once #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include class TCPSocket { public: TCPSocket() { if ((m_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { throw std::runtime_error("Can't create TCP socket"); } #if defined(HAVE_SO_NOSIGPIPE) int val = 1; if (setsockopt(m_sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) == SOCKET_ERROR) { throw std::runtime_error("Can't set SO_NOSIGPIPE"); } #endif } ~TCPSocket() { if (m_sock != -1) { ::close(m_sock); } } TCPSocket(const TCPSocket& other) = delete; TCPSocket& operator=(const TCPSocket& other) = delete; TCPSocket(TCPSocket&& other) { m_sock = other.m_sock; if (other.m_sock != -1) { other.m_sock = -1; } } TCPSocket& operator=(TCPSocket&& other) { m_sock = other.m_sock; if (other.m_sock != -1) { other.m_sock = -1; } return *this; } bool valid(void) const { return m_sock != -1; } void listen(int port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); const int reuse = 1; if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { throw std::runtime_error("Can't reuse address for TCP socket"); } if (::bind(m_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { close(); throw std::runtime_error("Can't bind TCP socket"); } if (::listen(m_sock, 1) < 0) { close(); m_sock = -1; throw std::runtime_error("Can't listen TCP socket"); } } void close(void) { ::close(m_sock); m_sock = -1; } TCPSocket accept_with_timeout(int timeout_ms, struct sockaddr_in *client) { struct pollfd fds[1]; fds[0].fd = m_sock; fds[0].events = POLLIN | POLLOUT; int retval = poll(fds, 1, timeout_ms); if (retval == -1) { throw std::runtime_error("TCP Socket accept error: " + std::to_string(errno)); } else if (retval) { socklen_t client_len = sizeof(struct sockaddr_in); int sockfd = accept(m_sock, (struct sockaddr*)&client, &client_len); TCPSocket s(sockfd); return s; } else { TCPSocket s(-1); return s; } } ssize_t sendall(const void *buffer, size_t buflen) { uint8_t *buf = (uint8_t*)buffer; while (buflen > 0) { /* On Linux, the MSG_NOSIGNAL flag ensures that the process * would not receive a SIGPIPE and die. * Other systems have SO_NOSIGPIPE set on the socket for the * same effect. */ #if defined(HAVE_MSG_NOSIGNAL) const int flags = MSG_NOSIGNAL; #else const int flags = 0; #endif ssize_t sent = ::send(m_sock, buf, buflen, flags); if (sent < 0) { return -1; } else { buf += sent; buflen -= sent; } } return buflen; } ssize_t recv(void *buffer, size_t length, int flags) { return ::recv(m_sock, buffer, length, flags); } private: explicit TCPSocket(int sockfd) { m_sock = sockfd; } int m_sock = -1; };