views:

639

answers:

2
s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.connect(Socket.pack_sockaddr_in('port', 'hostname'))

ssl = OpenSSL::SSL::SSLSocket.new(s, sslcert)
ssl.connect

from here on i would like to check in a thread if the ssl connection and the underlying socket is still ESTABLISHED or if it went into CLOSE_WAIT after the default of 7200 seconds or even worse got closed without actually needing to .write() to or .read() from it.

is it done with some select(), IO.select() or another method?

btw: the socket never receives any data it just sends some occasionally.

many thanks in advance.

+3  A: 

The answer is implementation specific. You'll need to check tcp implementation header files on your OS. Here is a sample client for linux that returns the socket state.

  ts = TCPSocket.new('localhost', 5777)
  ssl = OpenSSL::SSL::SSLSocket.new(ts, OpenSSL::SSL::SSLContext.new)
  ssl.sync = true
  ssl.connect
  # TCP_INFO is 11
  # note that TCP_INFO isn't defined in the ruby source.  
  # i had to look up the integer value in /usr/include/netinet/tcp.h
  optval = ts.getsockopt(Socket::SOL_TCP, 11) 
  state = optval.unpack "i" 
  puts "state: #{state}"

Here's the tcp_info struct for my up-to-date ubuntu linux

struct tcp_info
{
  u_int8_t      tcpi_state;
  u_int8_t      tcpi_ca_state;
  u_int8_t      tcpi_retransmits;
  u_int8_t      tcpi_probes;
  u_int8_t      tcpi_backoff;
  u_int8_t      tcpi_options;
  u_int8_t      tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;

  u_int32_t     tcpi_rto;
  u_int32_t     tcpi_ato;
  u_int32_t     tcpi_snd_mss;
  u_int32_t     tcpi_rcv_mss;

  u_int32_t     tcpi_unacked;
  u_int32_t     tcpi_sacked;
  u_int32_t     tcpi_lost;
  u_int32_t     tcpi_retrans;
  u_int32_t     tcpi_fackets;

  /* Times. */
  u_int32_t     tcpi_last_data_sent;
  u_int32_t     tcpi_last_ack_sent;     /* Not remembered, sorry.  */
  u_int32_t     tcpi_last_data_recv;
  u_int32_t     tcpi_last_ack_recv;

  /* Metrics. */
  u_int32_t     tcpi_pmtu;
  u_int32_t     tcpi_rcv_ssthresh;
  u_int32_t     tcpi_rtt;
  u_int32_t     tcpi_rttvar;
  u_int32_t     tcpi_snd_ssthresh;
  u_int32_t     tcpi_snd_cwnd;
  u_int32_t     tcpi_advmss;
  u_int32_t     tcpi_reordering;

  u_int32_t     tcpi_rcv_rtt;
  u_int32_t     tcpi_rcv_space;

  u_int32_t     tcpi_total_retrans;
};

You may note that my script only returns an integer. Here's the C enum that details the TCP states and their integer values. Again, this was found in /usr/include/netinet/tcp.h

enum
{   
  TCP_ESTABLISHED = 1,          
  TCP_SYN_SENT,
  TCP_SYN_RECV,
  TCP_FIN_WAIT1,
  TCP_FIN_WAIT2,
  TCP_TIME_WAIT,
  TCP_CLOSE,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_LISTEN, 
  TCP_CLOSING   /* now a valid state */
};

Also, this thread says that you can detect CLOSE_WAIT by reading EOF. But since you're concerned about whether data has been sent, you'll probably need to unpack up to tcpi_last_data_sent.

Finally, a caveat. I took on the challenge of answering your question 'cause it sounded fun and it was but my C legs are still wobbly so YMMV. :)

Jonathan R. Wallace
A: 

Thanks to put together such a useful answer Jonathan! I was looking for the solution for the same issue and this is exactly what I am looking for!

jimx
this should be a comment on the answer, like this comment
JustSmith