/src/lib/vio/TCP.cpp
https://gitlab.com/piconf/software · C++ · 308 lines · 240 code · 25 blank · 43 comment · 53 complexity · 68b12f886dca7cf10c78267340615d32 MD5 · raw file
- /******************************************************************************
- * This file is part of vio. *
- * *
- * Copyright (C) 2012, 2013, 2014 Rouven Spreckels <n3vu0r@nevux.org> *
- * *
- * vio is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License version 3 as *
- * published by the Free Software Foundation on 19 November 2007. *
- * *
- * vio 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 Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with vio. If not, see <http://www.gnu.org/licenses/>. *
- ******************************************************************************/
- #ifndef _VIO_TCP_
- #define _VIO_TCP_
- #include "TCP.hpp"
- #include <cerrno>
- #include <cstring>
- #include <cstdlib>
- #include <unistd.h>
- void VIO::TCP::readable() {
- if (state & LISTENING) {
- if (state & ACCEPTING)
- accept();
- else
- state |= ACCEPTABLE;
- }
- else {
- //Data front = destinations.front();
- IO::readable();
- if (!fetched.size)
- state &= ~CONNECTED;
- //else
- //if (!front.size)
- // if (0 > recv(fd, front.data, fetched.size, 0))
- // state &= ~CONNECTED;
- }
- }
- void VIO::TCP::writable() {
- if (state & CONNECTING) {
- int value = 0;
- socklen_t size = sizeof value;
- if (0 > getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &size))
- throw Error("Cannot get socket options", std::strerror(errno), that, GETSOCKOPT);
- if (value) {
- ::close(fd);
- if (address)
- connecting();
- else
- throw Error("No addresses left", 0, that, UNAVAILABLE);
- }
- else {
- freeaddrinfo(address);
- configure();
- select &= ~WRITABLE;
- state &= ~CONNECTING;
- state |= CONNECTED;
- }
- }
- else {
- IO::writable();
- if (!pushed.size)
- state &= ~CONNECTED;
- }
- }
- void VIO::TCP::timewise() {
- static char byte;
- if (!isConnected())
- throw Error("Timer disconnected", std::strerror(errno), that, TIMER);
- if (!hasToRead()) {
- fetch(&byte, sizeof byte);
- clock_gettime(CLOCK_REALTIME, &last);
- select |= SCHEDULE;
- }
- else
- select &= ~SCHEDULE;
- }
- void VIO::TCP::timing(TCP* tcp, timespec span) {
- char byte;
- for (bool undone = true; undone; undone = process()) {
- if (!tcp->isConnected())
- break;
- if (!tcp->hasToWrite()) {
- timespec left = span;
- while (-1 == nanosleep(&left, &left) and errno == EINTR);
- tcp->push(&byte, sizeof byte);
- }
- }
- }
- /*
- void VIO::TCP::read() {
- int bytes = recv(fd, destinations.front().data, destinations.front().left, destinations.front().size ? 0 : MSG_PEEK);
- if (0 > bytes)
- bytes = 0;
- fetched.data = destinations.front().data;
- fetched.size = bytes;
- fetched.left = 0;
- }
- void VIO::TCP::write() {
- int bytes = send(fd, sources.front().data, sources.front().left, 0);
- if (0 > bytes)
- bytes = 0;
- pushed.data = sources.front().data;
- pushed.size = bytes;
- pushed.left = 0;
- }
- */
- void VIO::TCP::read() {
- try {
- IO::read();
- }
- catch (IO::Error& error) {
- state &= ~CONNECTED;
- //throw error;
- }
- }
- void VIO::TCP::write() {
- try {
- IO::write();
- }
- catch (IO::Error& error) {
- state &= ~CONNECTED;
- //throw error;
- }
- }
- void VIO::TCP::connecting() {
- if (!address)
- throw Error("No addresses left", 0, that, UNAVAILABLE);
- for (; address; address = address->ai_next) {
- if (0 > (fd = ::socket(address->ai_family, address->ai_socktype, address->ai_protocol)))
- continue;
- configure();
- if (::connect(fd, address->ai_addr, address->ai_addrlen) < 0) {
- if (errno == EINPROGRESS) {
- address = address->ai_next;
- select |= WRITABLE;
- break;
- }
- ::close(fd);
- continue;
- }
- freeaddrinfo(address);
- configure();
- select &= ~WRITABLE;
- state &= ~CONNECTING;
- state |= CONNECTED;
- break;
- }
- }
- void VIO::TCP::pair(TCP* tcp0, TCP* tcp1) {
- int fd[2];
- if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd))
- throw Error("Cannot create socketpair", std::strerror(errno), 0, SOCKETPAIR);
- tcp0->fd = fd[0];
- tcp1->fd = fd[1];
- tcp0->configure();
- tcp1->configure();
- tcp0->state |= CONNECTED;
- tcp1->state |= CONNECTED;
- }
- void VIO::TCP::forkTimer(TCP* pulse, timespec span) {
- TCP timer;
- TCP::pair(pulse, &timer);
- pulse->select |= TIMEWISE;
- pid_t pid;
- if (-1 == (pid = fork()))
- throw Error("Cannot fork process", std::strerror(errno), 0, FORK);
- if (pid) {
- timer.close();
- timer.clear();
- pulse->timewise();
- }
- else {
- pulse->close();
- pulse->clear();
- timing(&timer, span);
- exit(EXIT_SUCCESS);
- }
- }
- VIO::TCP::Error::Error(const char* context, const char* message, const void* instance, int code)
- : IO::Error(context, message, instance, code) {
- }
- VIO::TCP::TCP()
- : IO() {
- state = 0;
- address = 0;
- addrlen = sizeof (struct sockaddr);
- }
- void VIO::TCP::listen(std::string service, bool reuse, bool accepting) {
- int value = reuse ? 1 : 0;
- state |= LISTENING;
- if (accepting)
- state |= ACCEPTING;
- select |= READABLE;
- struct addrinfo hints, * address;
- memset(&hints, 0, sizeof hints);
- hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
- hints.ai_socktype = SOCK_STREAM;
- if (int gaino = ::getaddrinfo(0, service.c_str(), &hints, &address))
- throw Error("Cannot resolve address information", gai_strerror(gaino), that, GETADDRINFO);
- for (; address; address = address->ai_next) {
- if (0 > (fd = ::socket(address->ai_family, address->ai_socktype, address->ai_protocol)))
- continue;
- if (reuse and -1 == ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof (int)))
- throw Error("Cannot set socket option reuse", std::strerror(errno), that, SETSOCKOPT);
- if (::bind(fd, address->ai_addr, address->ai_addrlen) < 0) {
- ::close(fd);
- fd = -1;
- continue;
- }
- }
- freeaddrinfo(address);
- if (0 > fd)
- throw Error("Cannot create listening socket", std::strerror(errno), that, BIND);
- if (-1 == ::listen(fd, SOMAXCONN))
- throw Error("Cannot listen to socket", std::strerror(errno), that, LISTEN);
- configure();
- }
- VIO::TCP::Clients::reverse_iterator VIO::TCP::accept() {
- if (!(state & (ACCEPTING | ACCEPTABLE)))
- return clients.rend();
- TCP* client = new TCP();
- if (-1 == (client->fd = ::accept(fd, (struct sockaddr*)&client->addr, &client->addrlen)))
- return clients.rend();
- if (-1 == getpeername(client->fd, (struct sockaddr*)&client->addr, &client->addrlen))
- throw Error("Cannot get peer name", std::strerror(errno), that, GETPEERNAME);
- state &= ~ACCEPTABLE;
- client->state |= CONNECTED;
- client->select |= ACTIVITY;
- client->activity();
- client->configure();
- clients.push_back(client);
- return clients.rbegin();
- }
- void VIO::TCP::connect(std::string host, std::string service) {
- struct addrinfo hints;
- memset(&hints, 0, sizeof hints);
- hints.ai_flags = AI_ADDRCONFIG;
- hints.ai_socktype = SOCK_STREAM;
- if(int gaino = ::getaddrinfo(host.c_str(), service.c_str(), &hints, &address))
- throw Error("Cannot resolve address information", gai_strerror(gaino), that, GETADDRINFO);
- state |= CONNECTING;
- connecting();
- select |= ACTIVITY;
- activity();
- }
- bool VIO::TCP::isConnected() {
- return state & CONNECTED ? true : false;
- }
- bool VIO::TCP::isConnected(timespec timeout) {
- if (!IO::isActive(timeout)) {
- if (state & CONNECTED) {
- state &= ~CONNECTED;
- ::close(fd);
- clear();
- }
- activity();
- throw Error("Inactivity reached timeout", 0, that, TIMEOUT);
- }
- return isConnected();
- }
- std::string VIO::TCP::host() {
- char host[NI_MAXHOST];
- addrlen = sizeof (struct sockaddr_storage);
- if (int gaino = getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof host, NULL, 0, 0))
- throw Error("Cannot resolve name information", gai_strerror(gaino), that, GETNAMEINFO);
- return host;
- }
- void VIO::TCP::clear() {
- IO::clear();
- state &= LISTENING | ACCEPTING;
- }
- VIO::TCP::~TCP() {
- if (state & LISTENING)
- for (Clients::iterator client = clients.begin(); client != clients.end(); ++client)
- delete *client;
- }
- #endif