/src/socket.cc
C++ | 794 lines | 466 code | 70 blank | 258 comment | 114 complexity | e53b9b138b1d46b1b393797683fa747d MD5 | raw file
- /*
- * socket.cc
- *
- * Copyright (c) 2012, Heiko Wundram.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of modelnine.org, protoproxy nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL HEIKO WUNDRAM BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Created on: 31.01.2012
- * Author: Heiko Wundram <modelnine@modelnine.org>
- */
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <cassert>
- #include <cerrno>
- #include <cstdarg>
- #include <cstddef>
- #include <ctime>
- #include <fcntl.h>
- #include <poll.h>
- #include <unistd.h>
- #include "exceptions.hh"
- #include "logger.hh"
- #include "reactor.hh"
- #include "socket.hh"
- #include "types.hh"
- using namespace protoproxy;
- using namespace std;
- /**
- * Handle attachment of socket to reactor.
- */
- void socket::handle_attach() {
- // Default does nothing.
- debug("Default attach action called on socket attach for socket at %p",
- static_cast<void*>(this));
- }
- /**
- * Handle error on socket.
- */
- void socket::handle_error() {
- // Check type of socket to handle error response.
- if( m_destroy ) {
- // Socket can be destroyed, do so on unhandled error.
- warning("Default socket error callback called on socket at %p,"
- " destroying destructible socket",
- static_cast<void*>(this));
- delete this;
- } else {
- // Just detach the socket on error callback, can't be destroyed.
- warning("Default socket error callback called on socket at %p,"
- " detaching non-destructible socket",
- static_cast<void*>(this));
- detach();
- }
- }
- /**
- * Handle read condition on socket.
- */
- void socket::handle_read() {
- // Throw exception.
- error("Default read callback called with socket event for socket at %p",
- static_cast<void*>(this));
- throw not_implemented_error("Read event called, but not implemented");
- }
- #if POLLRDBAND != POLLPRI
- /**
- * Handle priority read condition on socket.
- */
- void socket::handle_priority_read() {
- // Throw exception.
- error("Default priority read callback called with socket event for socket"
- " at %p", static_cast<void*>(this));
- throw not_implemented_error("Priority read event called, but not"
- " implemented");
- }
- #endif
- /**
- * Handle high priority read condition on socket.
- */
- void socket::handle_high_priority_read() {
- // Throw exception.
- error("Default high priority read callback called with socket event for"
- " socket at %p", static_cast<void*>(this));
- throw not_implemented_error("High priority read event called, but not"
- " implemented");
- }
- /**
- * Handle write condition on socket.
- */
- void socket::handle_write() {
- // Throw exception.
- error("Default write callback called with socket event for socket at %p",
- static_cast<void*>(this));
- throw not_implemented_error("Write event called, but not implemented");
- }
- #if POLLWRBAND != POLLWRNORM
- /**
- * Handle priority write condition on socket.
- */
- void socket::handle_priority_write() {
- // Throw exception.
- error("Default priority write callback called with socket event for socket"
- " at %p", static_cast<void*>(this));
- throw not_implemented_error("Priority write event called, but not"
- " implemented");
- }
- #endif
- /**
- * Handle hangup on socket, signalling that the socket is done.
- */
- void socket::handle_hangup() {
- // Default implementation does nothing on hangup.
- info("Default socket hangup callback called for socket at %p, doing"
- " nothing", static_cast<void*>(this));
- }
- /**
- * Handle timeout condition on socket timeout, dispatching the timeout id.
- */
- void socket::handle_timeout(const size_t id __attribute__((unused))) {
- // Throw exception.
- error("Default socket timeout callback called with no event attached for"
- " socket at %p", static_cast<void*>(this));
- throw not_implemented_error("Timeout event called, but not implemented");
- }
- /**
- * Handle detachment of the socket from a reactor.
- */
- void socket::handle_detach() {
- // Default does nothing.
- debug("Default socket detach callback called on socket detach for socket"
- " at %p", static_cast<void*>(this));
- }
- /**
- * Handle log message to socket with the specified arguments.
- */
- void socket::log(const loglevel level, const logdomain domain,
- const char* const msg, va_list args) {
- // Check for reactor, if so, dispatch it there.
- assert(msg);
- if( m_reactor )
- // Dispatch log message to reactor, reusing arguments.
- m_reactor->log(level, domain, msg, args);
- else {
- // TODO: need to think of something to do here.
- }
- }
- /**
- * Initialize a new socket which is not bound to a reactor or a file
- * descriptor.
- */
- socket::socket(const bool destroy, const logdomain defdomain) :
- logger(defdomain), m_destroy(destroy), m_reactor(0), m_prev(),
- m_next(), m_fd(-1), m_events(), m_nexttimeoutid(0), m_nexttimeout(0),
- m_lasttimeout() {
- // No specific construction.
- debug("New socket at %p, no bound socket", static_cast<void*>(this));
- }
- /**
- * Create a new socket of the specified socket type.
- */
- socket::socket(const int domain, const int type, const int protocol,
- const bool destroy, const logdomain defdomain) :
- logger(defdomain), m_destroy(destroy), m_reactor(0), m_prev(),
- m_next(), m_fd(), m_events(0), m_nexttimeoutid(0), m_nexttimeout(0),
- m_lasttimeout() {
- // Initialize descriptor.
- debug("New socket at %p, creating socket domain %i, type %i, protocol %i",
- static_cast<void*>(this), domain, type, protocol);
- if( ( m_fd = ::socket(domain, type, protocol) ) == -1 )
- throw os_error("Failed to initialize new socket of specified type");
- try {
- // Try to set new socket to non-blocking.
- int flags;
- if( ( flags = fcntl(m_fd, F_GETFL) ) == -1 )
- throw os_error("Failed to get socket flags");
- else if( fcntl(m_fd, F_SETFL, flags | O_NONBLOCK) == -1 )
- throw os_error("Failed to set socket to non-blocking");
- } catch( ... ) {
- // Clean up socket on error exit.
- ::close(m_fd);
- throw;
- }
- }
- /**
- * Initialize socket bound to existing file descriptor.
- */
- socket::socket(const int fd, const bool destroy, const logdomain defdomain) :
- logger(defdomain), m_destroy(destroy), m_reactor(0), m_prev(),
- m_next(), m_fd(fd), m_events(0), m_nexttimeoutid(0), m_nexttimeout(0),
- m_lasttimeout() {
- // Try to set socket to non-blocking; we don't close on error.
- debug("New socket at %p, using existing descriptor %i",
- static_cast<void*>(this), fd);
- assert(m_fd>=0);
- int flags;
- if( ( flags = fcntl(m_fd, F_GETFL) ) == -1 )
- throw os_error("Failed to get socket flags");
- else if( fcntl(m_fd, F_SETFL, flags | O_NONBLOCK) == -1 )
- throw os_error("Failed to set socket to non-blocking");
- }
- /**
- * Access reference to currently bound reactor.
- */
- reactor& socket::reactor_() const {
- // Return reactor.
- if( !m_reactor )
- throw state_error("Trying to access reactor when not set up");
- return *m_reactor;
- }
- /**
- * Set up event mask for socket to enable reads.
- */
- void socket::want_read(const bool read) {
- // Set up flag depending on boolean.
- if( m_fd < 0 )
- throw state_error("Trying to set up events on closed socket");
- else if( read ) {
- // Set the flag in event mask.
- debug("Setting read flag on socket event mask for socket at %p",
- static_cast<void*>(this));
- m_events |= POLLRDNORM;
- } else {
- // Clear the flag in event mask.
- debug("Clearing read flag on socket event mask for socket at %p",
- static_cast<void*>(this));
- m_events &= ~POLLRDNORM;
- }
- // Dispatch events to descriptor if appropriate.
- if( m_reactor ) {
- // Set up event flags in reactor polling descriptor.
- assert(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0&&
- (m_reactor->m_pollfds[m_fd].events&~POLLRDNORM)==(m_events&~POLLRDNORM));
- m_reactor->m_pollfds[m_fd].events = m_events;
- }
- }
- #if POLLRDBAND != POLLPRI
- /**
- * Set up event mask for socket to enable priority reads.
- */
- void socket::want_priority_read(const bool read) {
- // Set up flag depending on boolean.
- if( m_fd < 0 )
- throw state_error("Trying to set up events on closed socket");
- else if( read ) {
- // Set the flag in event mask.
- debug("Setting priority read flag on socket event mask for socket at"
- " %p", static_cast<void*>(this));
- m_events |= POLLRDBAND;
- } else {
- // Clear the flag in event mask.
- debug("Clearing priority read flag on socket event mask for socket at"
- " %p", static_cast<void*>(this));
- m_events &= ~POLLRDBAND;
- }
- // Dispatch events to descriptor if appropriate.
- if( m_reactor ) {
- // Set up event flags in reactor polling descriptor.
- assert(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0&&
- (m_reactor->m_pollfds[m_fd].events&~POLLRDBAND)==(m_events&~POLLRDBAND));
- m_reactor->m_pollfds[m_fd].events = m_events;
- }
- }
- #endif
- /**
- * Set up event mask for socket to enable high priority reads.
- */
- void socket::want_high_priority_read(const bool read) {
- // Set up flag depending on boolean.
- if( m_fd < 0 )
- throw state_error("Trying to set up events on closed socket");
- else if( read ) {
- // Set the flag in event mask.
- debug("Setting high priority read flag on socket event mask for"
- " socket at %p", static_cast<void*>(this));
- m_events |= POLLPRI;
- } else {
- // Clear the flag in event mask.
- debug("Clearing high priority read flag on socket event mask for"
- " socket at %p", static_cast<void*>(this));
- m_events &= ~POLLPRI;
- }
- // Dispatch events to descriptor if appropriate.
- if( m_reactor ) {
- // Set up event flags in reactor polling descriptor.
- assert(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0&&
- (m_reactor->m_pollfds[m_fd].events&~POLLPRI)==(m_events&~POLLPRI));
- m_reactor->m_pollfds[m_fd].events = m_events;
- }
- }
- /**
- * Set up event mask for socket to enable writes.
- */
- void socket::want_write(const bool write) {
- // Set up flag depending on boolean.
- if( m_fd < 0 )
- throw state_error("Trying to set up events on closed socket");
- else if( write ) {
- // Set the flag in event mask.
- debug("Setting write flag on socket event mask for socket at %p",
- static_cast<void*>(this));
- m_events |= POLLWRNORM;
- } else {
- // Clear the flag in event mask.
- debug("Clearing write flag on socket event mask for socket at %p",
- static_cast<void*>(this));
- m_events &= ~POLLWRNORM;
- }
- // Dispatch events to descriptor if appropriate.
- if( m_reactor ) {
- // Set up event flags in reactor polling descriptor.
- assert(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0&&
- (m_reactor->m_pollfds[m_fd].events&~POLLWRNORM)==(m_events&~POLLWRNORM));
- m_reactor->m_pollfds[m_fd].events = m_events;
- }
- }
- #if POLLWRBAND != POLLWRNORM
- /**
- * Set up event mask for socket to enable priority writes.
- */
- void socket::want_priority_write(const bool write) {
- // Set up flag depending on boolean.
- if( m_fd < 0 )
- throw state_error("Trying to set up events on closed socket");
- else if( write ) {
- // Set the flag in event mask.
- debug("Setting priority write flag on socket event mask for socket at"
- " %p", static_cast<void*>(this));
- m_events |= POLLWRBAND;
- } else {
- // Clear the flag in event mask.
- debug("Clearing priority write flag on socket event mask for socket at"
- " %p", static_cast<void*>(this));
- m_events &= ~POLLWRBAND;
- }
- // Dispatch events to descriptor if appropriate.
- if( m_reactor ) {
- // Set up event flags in reactor polling descriptor.
- assert(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0&&
- (m_reactor->m_pollfds[m_fd].events&~POLLWRBAND)==(m_events&~POLLWRBAND));
- m_reactor->m_pollfds[m_fd].events = m_events;
- }
- }
- #endif
- /**
- * Attach a new timeout callback to this socket, which fires after the
- * specified time.
- */
- size_t socket::addtimeout(const int msecs) {
- // Fetch new timeout object.
- assert(!m_nexttimeout||m_lasttimeout);
- socket_timeout* newtimeout;
- if( m_reactor && m_reactor->m_freetimeouts ) {
- // Fetch object from reactor and update new element.
- debug("Attaching new timeout %i on socket %p, reusing timeout object",
- msecs, static_cast<void*>(this));
- newtimeout = m_reactor->m_freetimeouts;
- m_reactor->m_freetimeouts = newtimeout->m_next;
- } else {
- // Need to allocate new timeout object; if we fail here, raise as is.
- debug("Attaching new timeout %i on socket %p, creating timeout object",
- msecs, static_cast<void*>(this));
- newtimeout = new socket_timeout();
- }
- try {
- // Fetch current time.
- if( m_reactor && m_reactor->m_running )
- // Can use reactor time to set up timeout.
- newtimeout->m_timeout = m_reactor->m_curtime;
- else if( clock_gettime(CLOCK_REALTIME, &newtimeout->m_timeout) == -1 )
- throw os_error("Failed to get current time from clock");
- } catch( ... ) {
- // Clean up timeout object that was created.
- if( m_reactor ) {
- // Push it to reactor queue.
- newtimeout->m_next = m_reactor->m_freetimeouts;
- m_reactor->m_freetimeouts = newtimeout;
- } else
- // Delete it (no reactor set up to attach it to).
- delete newtimeout;
- // Rethrow error.
- throw;
- }
- // Only add timeout if it is actually one (i.e., we're not setting
- // up a timeout in the past/now).
- if( msecs > 0 ) {
- // Initialize generic fields.
- newtimeout->m_timeout.tv_sec += msecs / 1000;
- newtimeout->m_timeout.tv_nsec += ( msecs % 1000 ) * 1000000l;
- newtimeout->m_id = m_nexttimeoutid++;
- // Limit timeout, calculating proper seconds (normalize it).
- if( newtimeout->m_timeout.tv_nsec >= 1000000000l ) {
- ++newtimeout->m_timeout.tv_sec;
- newtimeout->m_timeout.tv_nsec -= 1000000000l;
- }
- }
- // Walk list of timeouts, finding position.
- socket_timeout** instimeout = &m_nexttimeout;
- for( ; *instimeout; instimeout = &(*instimeout)->m_next ) {
- // Check next element for being after element to insert; if so,
- // stop advancing and do insert.
- const socket_timeout* const checktimeout = *instimeout;
- if( checktimeout->m_timeout.tv_sec > newtimeout->m_timeout.tv_sec ||
- ( checktimeout->m_timeout.tv_sec == newtimeout->m_timeout.tv_sec &&
- checktimeout->m_timeout.tv_nsec > newtimeout->m_timeout.tv_nsec ) )
- break;
- }
- // Update next of timeout fetched position.
- if( !( newtimeout->m_next = *instimeout ) ) {
- assert(!m_nexttimeout||m_lasttimeout==instimeout);
- m_lasttimeout = &newtimeout->m_next;
- }
- // Update new insert position, finalizing the after insert.
- debug("Attached timeout at %lu.%09lu as id %lu",
- newtimeout->m_timeout.tv_sec, newtimeout->m_timeout.tv_nsec,
- static_cast<unsigned long>(newtimeout->m_id));
- *instimeout = newtimeout;
- // Return timeout identifier.
- return newtimeout->m_id;
- }
- /**
- * Cancel timeout that's set up, walking tree and removing the specified
- * element.
- */
- void socket::canceltimeout(const size_t id) {
- // Walk list of timeouts, finding position.
- assert(!m_nexttimeout||m_lasttimeout);
- socket_timeout** deltimeout = &m_nexttimeout;
- for( ; *deltimeout; deltimeout = &(*deltimeout)->m_next ) {
- // Check whether this is the ID that matches.
- const socket_timeout* const checktimeout = *deltimeout;
- if( checktimeout->m_id == id )
- break;
- }
- // Check whether ID was found.
- socket_timeout* const remtimeout = *deltimeout;
- if( !remtimeout ) {
- debug("Failed to find timeout id %lu (not present), can't detach it",
- static_cast<unsigned long>(id));
- return;
- }
- // Update pointers.
- if( !( *deltimeout = remtimeout->m_next ) ) {
- // Need to update back of queue.
- assert(&remtimeout->m_next==m_lasttimeout);
- m_lasttimeout = deltimeout;
- }
- // Move timeout back to queue.
- if( m_reactor ) {
- // Push it to reactor.
- debug("Timeout %lu for %lu.%09lu detached from queue, put back on"
- " reactor set", remtimeout->m_timeout.tv_sec,
- remtimeout->m_timeout.tv_nsec, static_cast<unsigned long>(id));
- remtimeout->m_next = m_reactor->m_freetimeouts;
- m_reactor->m_freetimeouts = remtimeout;
- } else {
- // Destroy it, can't attach to reactor.
- debug("Timeout %lu for %lu.%09lu detached from queue, destroyed",
- remtimeout->m_timeout.tv_sec, remtimeout->m_timeout.tv_nsec,
- static_cast<unsigned long>(id));
- delete remtimeout;
- }
- }
- /**
- * Create a new socket of the specified domain, type and protocol, setting up
- * the descriptor state appropriately.
- */
- void socket::create(const int domain, const int type, const int protocol) {
- // Try to create the socket.
- if( m_fd >= 0 )
- throw state_error("Trying to create socket when socket is open");
- else if( ( m_fd = ::socket(domain, type, protocol) ) == -1 )
- throw os_error("Failed to initialize new socket of specified type");
- // Handle exceptions when attaching to clean up properly.
- debug("Created new socket for socket at %p, domain %i, type %i,"
- " protocol %i", static_cast<void*>(this), domain, type, protocol);
- try {
- // Try to set new socket to non-blocking.
- int flags;
- if( ( flags = fcntl(m_fd, F_GETFL) ) < 0 )
- throw os_error("Failed to get socket flags");
- else if( fcntl(m_fd, F_SETFL, flags | O_NONBLOCK) < 0 )
- throw os_error("Failed to set socket to non-blocking");
- // Check whether we need to acquire polling descriptor for it, and
- // reset events for newly created socket.
- m_events = 0;
- if( m_reactor )
- // Fetch poll descriptor by calling socket.
- m_fd = m_reactor->attachfd(m_fd, m_events);
- } catch( ... ) {
- // Clean up created descriptor on error (exceptions can only be
- // raised before the polling descriptor is successfully set up).
- ::close(m_fd);
- m_fd = -1;
- throw;
- }
- }
- /**
- * Set up socket option specified by the arguments.
- */
- void socket::setsockopt(const int level, const int optname, const int& val) {
- // Check for open socket.
- if( m_fd < 0 )
- throw state_error("Trying to bind socket when not created");
- // Set up socket option appropriately.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- if( ::setsockopt(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, level, optname, &val, sizeof(val)) == -1 )
- throw os_error("Failed to set socket option to specified value");
- debug("Set socket option %i/%i to %i for socket at %p", level, optname,
- val, static_cast<void*>(this));
- }
- /**
- * Bind open socket to the specified address.
- */
- void socket::bind(const sockaddr& addr, const socklen_t addrlen) {
- // Check for open socket.
- if( m_fd < 0 )
- throw state_error("Trying to bind socket when not created");
- // Bind according to reactor binding.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- if( ::bind(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, &addr, addrlen) == -1 )
- throw os_error("Failed to bind socket to passed address");
- debug("Bound socket at %p to specified address", static_cast<void*>(this));
- }
- /**
- * Set socket to listening state.
- */
- void socket::listen(const int backlog) {
- // Check for open socket.
- if( m_fd < 0 )
- throw state_error("Trying to bind socket when not created");
- // Set up listening state.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- if( ::listen(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, backlog) == -1 )
- throw os_error("Failed to initialize listening on socket");
- debug("Set up listening state with %i for socket at %p", backlog,
- static_cast<void*>(this));
- }
- /**
- * Accept incoming connection on the current socket, returning the accepted
- * file descriptor.
- */
- int socket::accept(sockaddr& addr, socklen_t& addrlen) {
- // Check for open socket.
- assert(addrlen>0);
- if( m_fd < 0 )
- throw state_error("Trying to bind socket when not created");
- // Accept connection.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- int fd;
- if( ( fd = ::accept(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, &addr, &addrlen) ) == -1 )
- throw os_error("Failed to accept new socket");
- // Return descriptor of accepted socket.
- debug("Accepted new incoming connection for socket at %p with fd %i",
- static_cast<void*>(this), fd);
- return fd;
- }
- /**
- * Read data from socket, setting it up in buffer.
- */
- ssize_t socket::read(void* const buf, const size_t len) {
- // Check for open socket.
- assert(!buf||len);
- if( m_fd < 0 )
- throw state_error("Trying to read from socket when not created");
- // Receive from socket.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- ssize_t rcvd;
- if( ( rcvd = ::recv(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, buf, len, 0) ) == -1 ) {
- // Check type of error.
- if( errno == EAGAIN || errno == EWOULDBLOCK )
- return 0;
- throw os_error("Failed to receive data on socket");
- }
- // Return received count, accounting for stream close.
- return rcvd > 0 ? rcvd : -1;
- }
- /**
- * Write data to socket, returning sent count.
- */
- ssize_t socket::write(const void* const buf, const size_t len) {
- // Check for open socket.
- assert(!buf||len);
- if( m_fd < 0 )
- throw state_error("Trying to write to socket when not created");
- // Send over socket.
- assert(!m_reactor||(m_reactor->m_pollfds&&
- static_cast<size_t>(m_fd)<m_reactor->m_pollfdsize&&
- m_reactor->m_pollfds[m_fd].fd>=0));
- ssize_t sent;
- if( ( sent = ::send(m_reactor ? m_reactor->m_pollfds[m_fd].fd : m_fd, buf, len, MSG_NOSIGNAL) ) == -1 ) {
- // Check type of error.
- if( errno == EAGAIN || errno == EWOULDBLOCK )
- return 0;
- throw os_error("Failed to send data on socket");
- }
- // Return actual sent count, accounting for stream close.
- return sent > 0 ? sent : -1;
- }
- /**
- * Close the current socket, cleaning up any remaining internal state.
- */
- void socket::close() {
- // Check for open socket and/or detach from reactor if required.
- if( m_fd < 0 )
- throw state_error("Trying to close socket when not created");
- else if( m_reactor )
- // Need to remove poll descriptor, store currently bound file
- // descriptor locally.
- m_fd = m_reactor->detachfd(m_fd);
- // Clean up the socket by closing it and resetting internal state.
- const int rv = ::close(m_fd);
- m_fd = -1;
- // Check for error on close.
- debug("Closed opened socket at %p", static_cast<void*>(this));
- if( rv < 0 )
- throw os_error("Failed to close open descriptor");
- }
- /**
- * Clean up socket by destroying remaining internal state.
- */
- socket::~socket() {
- // Check for detached socket; if not, detach socket now.
- if( m_reactor ) {
- // Detach descriptor if required, and detach socket from reactor
- // chain.
- if( m_fd >= 0 )
- m_fd = m_reactor->detachfd(m_fd);
- m_reactor->detachsock(this);
- // Place chain of timeout callbacks on reactor.
- if( m_nexttimeout ) {
- // Place all timeouts on reactor.
- assert(m_lasttimeout&&!*m_lasttimeout);
- *m_lasttimeout = m_reactor->m_freetimeouts;
- m_reactor->m_freetimeouts = m_nexttimeout;
- }
- } else {
- // Loop while timeout objects remain, destroying them.
- while( m_nexttimeout ) {
- // Fetch timeout.
- socket_timeout* const nexttimeout = m_nexttimeout;
- m_nexttimeout = nexttimeout->m_next;
- assert(m_nexttimeout||&nexttimeout->m_next==m_lasttimeout);
- // Destroy object.
- delete nexttimeout;
- }
- }
- // Close socket if not closed yet as final step, ignoring errors here.
- if( m_fd >= 0 )
- ::close(m_fd);
- }
- /**
- * Check whether socket is still opened.
- */
- bool socket::open() const {
- // Check open state.
- return m_fd >= 0;
- }
- /**
- * Check whether socket is currently attached.
- */
- bool socket::attached() const {
- // Check for reactor setup.
- return m_reactor;
- }
- /**
- * Detach socket from reactor, releasing any internal state that binds the
- * socket to the reactor.
- */
- void socket::detach() {
- // Check for bound reactor.
- if( !m_reactor )
- throw state_error("Trying to remove socket from reactor when not"
- " bound");
- else if( m_fd >= 0 )
- // Detach socket from reactor by unbinding it.
- m_fd = m_reactor->detachfd(m_fd);
- // Detach socket from reactor.
- m_reactor->detachsock(this);
- m_reactor = 0;
- // Dispatch callback to signal detachment.
- debug("Detached socket at %p from reactor", static_cast<void*>(this));
- handle_detach();
- }