tcp_socket::tcp_socket(int fd, unsigned int bufcnt): resource(fd) { if( bufcnt > MAX_BUFCNT ) throw std::logic_error("tcp_socket: Too many buffers"); m_free_buffers.reserve(bufcnt); m_pending.reserve(bufcnt); int v = 1; if( setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &v, sizeof(v)) == -1 ) throw_unix_error(errno, "setsockopt(SO_KEEPALIVE)"); v = 1; if( setsockopt(fd, IPPROTO_TCP, TCP_CORK, &v, sizeof(v)) == -1 ) throw_unix_error(errno, "setsockopt(TCP_CORK)"); v = KEEPALIVE_IDLE; if( setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &v, sizeof(v)) == -1 ) throw_unix_error(errno, "setsockopt(TCP_KEEPIDLE)"); v = KEEPALIVE_INTERVAL; if( setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &v, sizeof(v)) == -1 ) throw_unix_error(errno, "setsockopt(TCP_KEEPINTVL)"); for( unsigned int i = 0; i < bufcnt; ++i ) { char* p = (char*)MALLOC(SENDBUF_SIZE); if( p == NULL ) { for( char* p2: m_free_buffers ) FREE(p2); m_free_buffers.clear(); throw std::runtime_error("tcp_socket: failed to allocate buffers"); } // reserved, hence no throw m_free_buffers.push_back(p); } }
bool tcp_server_socket::on_readable() { while( true ) { union { struct sockaddr sa; struct sockaddr_storage ss; } addr; socklen_t addrlen = sizeof(addr); #ifdef _GNU_SOURCE int s = ::accept4(m_fd, &(addr.sa), &addrlen, SOCK_NONBLOCK|SOCK_CLOEXEC); #else int s = ::accept(m_fd, &(addr.sa), &addrlen); if( s != -1 ) { int fl = fcntl(s, F_GETFL, 0); if( fl == -1 ) fl = 0; if( fcntl(s, F_SETFL, fl | O_NONBLOCK) == -1 ) { ::close(s); throw_unix_error(errno, "fcntl(F_SETFL)"); } fl = fcntl(s, F_GETFD, 0); if( fl == -1 ) fl = 0; if( fcntl(s, F_SETFD, fl | FD_CLOEXEC) == -1 ) { ::close(s); throw_unix_error(errno, "fcntl(F_SETFD)"); } } #endif if( s == -1 ) { if( errno == EINTR || errno == ECONNABORTED ) continue; if( errno == EMFILE || errno == ENFILE ) { logger::error() << "accept: Too many open files."; continue; } if( errno == EAGAIN || errno == EWOULDBLOCK ) break; throw_unix_error(errno, "accept"); } try { std::unique_ptr<tcp_socket> t = m_wrapper(s, ip_address(&(addr.sa))); if( t.get() == nullptr ) { ::close(s); } else { m_reactor->add_resource( std::move(t), reactor::EVENT_IN|reactor::EVENT_OUT ); } } catch( ... ) { ::close(s); throw; } } return true; }
void _flush() { // with TCP_CORK, setting TCP_NODELAY effectively flushes // the kernel send buffer. int v = 1; if( setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)) == -1 ) throw_unix_error(errno, "setsockopt(TCP_NODELAY)"); }
int setup_server_socket(const char* bind_addr, std::uint16_t port) { struct addrinfo hint, *res; std::string s_port = std::to_string(port); std::memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_INET6; // can accept IPv4 address hint.ai_socktype = SOCK_STREAM; hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; int e = getaddrinfo(bind_addr, s_port.c_str(), &hint, &res); if( e == EAI_FAMILY || e == EAI_ADDRFAMILY || e == EAI_NODATA ) { logger::info() << "Binding to IPv6 fails, trying IPv4..."; hint.ai_family = AF_INET; e = getaddrinfo(bind_addr, s_port.c_str(), &hint, &res); } if( e == EAI_SYSTEM ) { throw_unix_error(errno, "getaddrinfo"); } else if( e != 0 ) { throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(e)); } int s = ::socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, res->ai_protocol); if( s == -1 ) { freeaddrinfo(res); throw_unix_error(errno, "socket"); } // intentionally ignore errors int ok = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &ok, sizeof(ok)); if( bind(s, res->ai_addr, res->ai_addrlen) == -1 ) { ::close(s); freeaddrinfo(res); throw_unix_error(errno, "bind"); } freeaddrinfo(res); if( listen(s, 128) == -1 ) { ::close(s); throw_unix_error(errno, "listen"); } return s; }
std::unique_ptr<signal_reader> signal_setup(std::initializer_list<int> sigs) { sigset_t mask[1]; sigemptyset(mask); for( int i: sigs ) sigaddset(mask, i); int e = pthread_sigmask(SIG_BLOCK, mask, NULL); if( e != 0 ) throw_unix_error(e, "pthread_sigmask"); // signal disposition is a per-process attribute. struct sigaction act; std::memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; if( sigaction(SIGPIPE, &act, NULL) == -1 ) throw_unix_error(errno, "sigaction"); std::memset(&act, 0, sizeof(act)); act.sa_handler = handle_abort; act.sa_flags = SA_RESETHAND; if( sigaction(SIGABRT, &act, NULL) == -1 ) throw_unix_error(errno, "sigaction"); return std::unique_ptr<signal_reader>( new signal_reader(mask) ); }
int tcp_connect(const char* node, std::uint16_t port, unsigned int timeout) { std::string s_port = std::to_string(port); struct addrinfo hint, *res; std::memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_INET; // prefer IPv4 hint.ai_socktype = SOCK_STREAM; hint.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG; int e = getaddrinfo(node, s_port.c_str(), &hint, &res); if( e == EAI_FAMILY || e == EAI_ADDRFAMILY || e == EAI_NODATA ) { hint.ai_family = AF_INET6; hint.ai_flags |= AI_V4MAPPED; e = getaddrinfo(node, s_port.c_str(), &hint, &res); } if( e == EAI_SYSTEM ) { throw_unix_error(errno, "getaddrinfo"); } else if( e != 0 ) { throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(e)); } int s = ::socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, res->ai_protocol); if( s == -1 ) { freeaddrinfo(res); throw_unix_error(errno, "socket"); } e = ::connect(s, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if( e == 0 ) return s; if( errno != EINPROGRESS ) { ::close(s); throw_unix_error(errno, "connect"); } struct pollfd fds; fds.fd = s; fds.events = POLLOUT; int poll_timeout = timeout ? timeout*1000 : -1; int n = ::poll(&fds, 1, poll_timeout); if( n == 0 ) { // timeout ::close(s); return -1; } if( n == -1 ) { ::close(s); throw_unix_error(errno, "poll"); } if( fds.revents & (POLLERR|POLLHUP|POLLNVAL) ) { ::close(s); return -1; } socklen_t l = sizeof(e); if( getsockopt(s, SOL_SOCKET, SO_ERROR, &e, &l) == -1 ) { ::close(s); throw_unix_error(errno, "getsockopt(SO_ERROR)"); } if( e != 0 ) { ::close(s); return -1; } return s; }