X-Git-Url: http://sraa.de/git/?a=blobdiff_plain;f=dol%2Fsrc%2Fdol%2Fvisitor%2Fhdsd%2Fscd%2Fscd_socket.cpp;fp=dol%2Fsrc%2Fdol%2Fvisitor%2Fhdsd%2Fscd%2Fscd_socket.cpp;h=a5ca5de71a32103e77964716313a329f4e0f0b6f;hb=8c411cf24ed0eb889191aaeafd8fa1e69081df42;hp=0000000000000000000000000000000000000000;hpb=dea7a4fb1ed110d3ce6e6d9255103d724bd66c0e;p=jump.git diff --git a/dol/src/dol/visitor/hdsd/scd/scd_socket.cpp b/dol/src/dol/visitor/hdsd/scd/scd_socket.cpp new file mode 100644 index 0000000..a5ca5de --- /dev/null +++ b/dol/src/dol/visitor/hdsd/scd/scd_socket.cpp @@ -0,0 +1,439 @@ +#include "scd_socket.h" + +#include +#include // for TCP_NODELAY +#include +#include +#include + +#include "scd_logging.h" +#include "scd_exception.h" + +scd_socket::scd_socket() +{ + _socket = -1; + _is_connecting = false; + _is_connected = false; +} + +scd_socket::~scd_socket() {} + +bool scd_socket::is_valid() const +{ + return (_socket != -1); +} + +bool scd_socket::create() +{ + // check if socket already created before + if ( is_valid() ) + { + return false; + } + + // create TCP socket + _socket = socket(PF_INET, SOCK_STREAM, 0); + + assert(is_valid()); + + _set_blocking(false); + + _is_connecting = false; + _is_connected = false; + + return true; + +} // create() + +void scd_socket::close() +{ + if ( is_valid() ) + { + ::close(_socket); + } + + _socket = -1; + _is_connecting = false; + _is_connected = false; +} + +bool scd_socket::bind(const std::string &loc_name, const uint16_t loc_port) +{ + int ret; + + if ( ! is_valid() ) + { + return false; + } + + // prepare the local address + sockaddr_in loc_addr; + if ( !_get_sockaddr(loc_addr, loc_name, loc_port) ) + { + return false; + } + + // set socket reusable (to recreate listener without timeout) + int on = 1; + ret = setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ); + if (ret == -1) + { + throw scd_exception("setsockopt()", errno); + } + + ret = ::bind( _socket, reinterpret_cast(&loc_addr), + sizeof( loc_addr ) ); + + if ( ret == -1 ) + { + switch(errno) + { + case EACCES: + scd_error("must be root to bind to desired port"); + break; + case EADDRINUSE: + scd_error("unable to bind as the address is already in use"); + break; + default: + throw scd_exception("bind()", errno); + } + return false; + } + + return true; + +} // bind() + + +bool scd_socket::bind(const uint16_t loc_port) +{ + return bind("", loc_port); +} // bind() + + +bool scd_socket::listen() +{ + int ret; + + if ( !is_valid() ) + { + return false; + } + + ret = ::listen(_socket, SCD_MAXCONN); + + if (ret == -1) + { + throw scd_exception("listen()", errno); + } + + return true; +} // listen() + +bool scd_socket::accept(scd_socket &new_sock) +{ + int ret; + + if ( !is_valid() ) + { + return false; + } + + ret = ::accept(_socket, NULL, NULL); + + if (ret < 0) + { + switch (errno) + { + case EAGAIN: + case ECONNABORTED: + case EINTR: + case EPROTO: + return false; + break; + default: + throw scd_exception("accept()", errno); + break; + } + } + else + { + new_sock._is_connected = true; + new_sock._socket = ret; + new_sock._set_blocking(false); + #ifdef TCP_NODELAY + new_sock._set_nodelay(); + #endif + } + + return true; + +} // accept() + + +bool scd_socket::connect(const std::string &rem_name, const uint16_t rem_port) +{ + int ret; + + if ( !is_valid() || _is_connecting || _is_connected ) + return false; + + sockaddr_in rem_addr; + if ( !_get_sockaddr(rem_addr, rem_name, rem_port) ) + return false; + + ret = ::connect(_socket, reinterpret_cast(&rem_addr), + sizeof(rem_addr)); + + if (ret == -1) + { + switch(errno) + { + case EINPROGRESS: + _is_connecting = true; + break; + case ECONNABORTED: + case ECONNREFUSED: + case EINTR: + case ENETUNREACH: + case ETIMEDOUT: + break; + case EALREADY: // should be prevented by _is_connecting + default: + throw scd_exception("connect()", errno); + break; + } + } + else // ret == 0 + { + _is_connected = true; + _is_connecting = false; + } + + return true; + +} // connect() + +bool scd_socket::is_connecting() +{ + if ( !is_valid() || !_is_connecting || _is_connected ) + return false; + + return true; + +} // is_connecting() + + +bool scd_socket::is_connected() +{ + if ( !is_valid() || _is_connecting || !_is_connected) + return false; + + return true; + +} // is_connecting() + + +bool scd_socket::connected_event() +{ + if (_is_connected) + return true; + + if ( !is_valid() ) + { + scd_warn("write event occured on an invalid socket"); + return false; + } + else if ( !_is_connecting ) + { + scd_warn("checking connected event while not connecting"); + return false; + } + + // get possible errors from connection attempt + int err, ret; + socklen_t err_size = sizeof(err); + ret = getsockopt(_socket, SOL_SOCKET, SO_ERROR, &err, &err_size); + + if (ret == -1) + { + throw scd_exception("getsockopt()", errno); + } + + // handle errors + switch(err) + { + case 0: + // connection established + _is_connecting = false; + _is_connected = true; + #ifdef TCP_NODELAY + _set_nodelay(); + #endif + return true; + break; + case ECONNABORTED: + case ECONNREFUSED: + case EINTR: + case ENETUNREACH: + case ETIMEDOUT: + // connection attempt failed + _is_connecting = false; + return false; + break; + case EINPROGRESS: + // should not be possible as we received a completion event + throw scd_exception("checking connected event while still connecting"); + break; + case EALREADY: // should be prevented by _is_connecting + default: + throw scd_exception("connect()", errno); + break; + } + + // not reached +} //connected_event() + + +size_t scd_socket::send(const void* buf, size_t len) +{ + ssize_t ret; + + if ( !is_valid() || len == 0 || !_is_connected ) + return 0; + + ret = ::send(_socket, buf, len, MSG_NOSIGNAL); + + if (ret < 0) + { + switch(errno) + { + case ECONNRESET: + case EPIPE: + case ETIMEDOUT: + close(); + break; + case EWOULDBLOCK: + break; + default: + throw scd_exception("send()", errno); + break; + } + return 0; + } + else + return ret; + +} // send() + +size_t scd_socket::recv(void* buf, size_t len) +{ + ssize_t ret; + + if ( !is_valid() || len == 0 || !_is_connected) + return 0; + + ret = ::recv(_socket, buf, len, MSG_NOSIGNAL); + + if (ret < 0) + { + switch(errno) + { + case ETIMEDOUT: + close(); + break; + case EAGAIN: + case EINTR: + break; + case ENOTCONN: + default: + throw scd_exception("recv()", errno); + break; + } + return 0; + } + else if (ret == 0) + { + close(); + return 0; + } + else + return ret; + +} // recv() + +bool scd_socket::_get_sockaddr(sockaddr_in &addr, const std::string &name, + const uint16_t port) const +{ + // flush addr + memset( &addr, 0, sizeof(addr) ); + + /* set content */ + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + if ( name.empty() ) + { + // bind to all local addresses + addr.sin_addr.s_addr = INADDR_ANY; + } + else + { + // bint to specified address only + struct hostent* he; + + // resolve name + he = gethostbyname2(&name[0], AF_INET); + if (he == NULL) + return false; + + // copy address (network order) + addr.sin_addr.s_addr = + reinterpret_cast(he->h_addr_list[0])->s_addr; + } + + return true; + +} // _get_sockaddr() + + +bool scd_socket::_set_blocking(const bool mode) +{ + if ( !is_valid() ) + { + return false; + } + + int opts = fcntl( _socket, F_GETFL ); + + if ( opts < 0 ) + { + throw scd_exception("fcntl()", errno); + } + + if ( !mode ) + opts = ( opts | O_NONBLOCK ); + else + opts = ( opts & ~O_NONBLOCK ); + + fcntl( _socket, F_SETFL,opts ); + + return true; + +} //_set_blocking() + + +bool scd_socket::_set_nodelay() +{ + if ( !is_valid()) + return false; + + // disable Nagle algorithm + int on = 1; + int ret = setsockopt( _socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) ); + if (ret == -1) + { + throw scd_exception("setsockopt()", errno); + } + + return true; +}