uint32_t TSocket::write_partial(const uint8_t* buf, uint32_t len) { if (socket_ < 0) { throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket"); } uint32_t sent = 0; int flags = 0; #ifdef MSG_NOSIGNAL // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we // check for the EPIPE return condition and close the socket in that case flags |= MSG_NOSIGNAL; #endif // ifdef MSG_NOSIGNAL int b = send(socket_, buf + sent, len - sent, flags); ++g_socket_syscalls; if (b < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return 0; } // Fail on a send error int errno_copy = errno; GlobalOutput.perror("TSocket::write_partial() send() " + getSocketInfo(), errno_copy); if (errno_copy == EPIPE || errno_copy == ECONNRESET || errno_copy == ENOTCONN) { close(); throw TTransportException(TTransportException::NOT_OPEN, "write() send() " + getSocketInfo(), errno_copy); } throw TTransportException(TTransportException::UNKNOWN, "write() send() " + getSocketInfo(), errno_copy); } // Fail on blocked send if (b == 0) { throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0."); } return b; }
void TSocket::setNoDelay(bool noDelay) { if (socket_ >= 0) { // Set socket to NODELAY int v = noDelay ? 1 : 0; int ret = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::setNoDelay() setsockopt() " + getSocketInfo(), errno_copy); return; } } options_.noDelay = noDelay; }
void TSocket::setLinger(bool on, int linger) { if (socket_ >= 0) { struct linger l = {(on ? 1 : 0), linger}; int ret = setsockopt(socket_, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::setLinger() setsockopt() " + getSocketInfo(), errno_copy); return; } } options_.lingerOn = on; options_.lingerVal = linger; }
void TSocket::write(const uint8_t* buf, uint32_t len) { uint32_t sent = 0; while (sent < len) { uint32_t b = write_partial(buf + sent, len - sent); if (b == 0) { // This should only happen if the timeout set with SO_SNDTIMEO expired. // Raise an exception. // However, first set SO_LINGER to 0 seconds for this socket. We think // the remote endpoint is not responding, so when we call close() we just // want the server to drop any untransmitted data immediately, instead of // moving into the TCP_FIN_WAIT1 state and continuing to try and send it. setLinger(true, 0); throw TTransportException(TTransportException::TIMED_OUT, "send timeout expired " + getSocketInfo()); } sent += b; } }
void TSocket::setRecvTimeout(int ms) { if (ms < 0) { char errBuf[512]; sprintf(errBuf, "TSocket::setRecvTimeout with negative input: %d\n", ms); GlobalOutput(errBuf); return; } if (socket_ >= 0) { struct timeval r = {(int)(ms/1000), (int)((ms%1000)*1000)}; int ret = setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &r, sizeof(r)); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::setRecvTimeout() setsockopt() " + getSocketInfo(), errno_copy); return; } } options_.recvTimeout = ms; }
void TSocket::setSendBufSize(size_t bufsize) { if (isOpen()) { // It is undesirable to reduce the send buffer at runtime. // The kernel may prevent the applicaton from sending new data // as it reduces the buffer if (bufsize < options_.sendBufSize) { GlobalOutput.printf("Error cannot reduce send buffer size of \ open socket old: %zu new: %zu", options_.sendBufSize, bufsize); return; } int ret = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::setSendBufSize() setsockopt() " + getSocketInfo(), errno_copy); return; } }
TSocket::Options TSocket::getCurrentSocketOptions() { TSocket::Options ro; socklen_t ol; socklen_t *optlen = &ol; struct timeval s; size_t bufSize; struct linger l; int ret = 0; int errno_copy; // ConnTimeout ro.connTimeout = options_.connTimeout; //sendTimeout s.tv_sec = s.tv_usec = 0; ret = 0; *optlen = sizeof(s); ret = getsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, &s, optlen); if (ret == -1) { errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::SendTimeout getsockopt() " + getSocketInfo(), errno_copy); } else { ro.sendTimeout = msTimeFromTimeval(s); } // recvTimeout s.tv_sec = s.tv_usec = 0; ret = 0; *optlen = sizeof(s); ret = getsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &s, optlen); if (ret == -1) { errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::RecvTimeout getsockopt() " + getSocketInfo(), errno_copy); } else { ro.recvTimeout = msTimeFromTimeval(s); } // sendBufSize ret = 0; bufSize = 0; *optlen = sizeof (bufSize); ret = getsockopt(socket_, SOL_SOCKET, SO_SNDBUF, &bufSize, optlen); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::getSendBufSize() setsockopt() " + getSocketInfo(), errno_copy); } else { ro.sendBufSize = bufSize; } // recvBufSize ret = 0; bufSize = 0; *optlen = sizeof (bufSize); ret = getsockopt(socket_, SOL_SOCKET, SO_RCVBUF, &bufSize, optlen); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::getRecvBufSize() setsockopt() " + getSocketInfo(), errno_copy); } else { ro.recvBufSize = bufSize; } // linger ret = 0; *optlen = sizeof (l); ret = getsockopt(socket_, SOL_SOCKET, SO_LINGER, &l, optlen); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::getLinger() setsockopt() " + getSocketInfo(), errno_copy); } else { ro.lingerOn = l.l_onoff; ro.lingerVal = l.l_linger; } // NODELAY int v = 0; ret = 0; *optlen = sizeof (v); ret = getsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &v, optlen); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::getNoDelay() setsockopt() " + getSocketInfo(), errno_copy); } else { ro.noDelay = v; } // REUSEADDR v = 0; ret = 0; *optlen = sizeof(v); ret = getsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &v, optlen); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::getCurrentSocketOptions() SO_REUSEADDR " + getSocketInfo(), errno_copy); } else { ro.reuseAddr = v; } return ro; }
uint32_t TSocket::read(uint8_t* buf, uint32_t len) { if (socket_ < 0) { throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket"); } int32_t retries = 0; // EAGAIN can be signaled both when a timeout has occurred and when // the system is out of resources (an awesome undocumented feature). // The following is an approximation of the time interval under which // EAGAIN is taken to indicate an out of resources error. uint32_t eagainThresholdMicros = 0; if (options_.recvTimeout) { // if a readTimeout is specified along with a max number of recv retries, then // the threshold will ensure that the read timeout is not exceeded even in the // case of resource errors eagainThresholdMicros = (options_.recvTimeout*1000)/ ((maxRecvRetries_>0) ? maxRecvRetries_ : 2); } apache::thrift::util::PausableTimer pausableTimer(options_.recvTimeout); try_again: // When there is a recv timeout set, an EINTR will restart the // recv() and hence reset the timer. So if we receive EINTRs at a // faster rate than the timeout value, the timeout will never // trigger. Therefore, we keep track of the total amount of time // we've spend in recv(), and if this value exceeds the timeout then // we stop retrying on EINTR. Note that we might still exceed the // timeout, but by at most a factor of 2. pausableTimer.start(); int got = folly::to<int>(recv(socket_, buf, len, 0)); int errno_after_recv = errno; // gettimeofday, used by PausableTimer, can change errno pausableTimer.stop(); ++g_socket_syscalls; // Check for error on read if (got < 0) { if (errno_after_recv == EAGAIN) { // if no timeout we can assume that resource exhaustion has occurred. if (options_.recvTimeout == 0) { throw TTransportException(TTransportException::TIMED_OUT, "EAGAIN (unavailable resources)"); } // check if this is the lack of resources or timeout case if (pausableTimer.didLastRunningTimeExceedLimit(eagainThresholdMicros)) { // infer that timeout has been hit throw TTransportException(TTransportException::TIMED_OUT, "EAGAIN (timed out) " + getSocketInfo()); } else { if (retries++ < maxRecvRetries_) { usleep(50); goto try_again; } else { throw TTransportException(TTransportException::TIMED_OUT, "EAGAIN (unavailable resources)"); } } } // If interrupted, try again, but only if we haven't exceeded the // timeout value yet. if (errno_after_recv == EINTR) { if (pausableTimer.hasExceededTimeLimit()) { // Exceeded the timeout value, this is a real retry now. if (retries++ < maxRecvRetries_) { pausableTimer.reset(); goto try_again; } else { throw TTransportException( TTransportException::TIMED_OUT, "recv() failed (EINTRs, then timed out)", errno_after_recv); } } else { goto try_again; } } #if defined __FreeBSD__ || defined __MACH__ if (errno_after_recv == ECONNRESET) { /* shigin: freebsd doesn't follow POSIX semantic of recv and fails with * ECONNRESET if peer performed shutdown * edhall: eliminated close() since we do that in the destructor. */ return 0; } #endif // Now it's not a try again case, but a real probblez GlobalOutput.perror("TSocket::read() recv() " + getSocketInfo(), errno_after_recv); // If we disconnect with no linger time if (errno_after_recv == ECONNRESET) { throw TTransportException(TTransportException::NOT_OPEN, "ECONNRESET " + getSocketInfo()); } // This ish isn't open if (errno_after_recv == ENOTCONN) { throw TTransportException(TTransportException::NOT_OPEN, "ENOTCONN " + getSocketInfo()); } // Timed out! if (errno_after_recv == ETIMEDOUT) { throw TTransportException(TTransportException::TIMED_OUT, "ETIMEDOUT " + getSocketInfo()); } // Some other error, whatevz throw TTransportException(TTransportException::UNKNOWN, "Unknown " + getSocketInfo(), errno_after_recv); } // The remote host has closed the socket if (got == 0) { // edhall: we used to call close() here, but our caller may want to deal // with the socket fd and we'll close() in our destructor in any case. return 0; } // Pack data into string return got; }