title: SO_KEEPALIVE does not work during a call to write()?
body: <p>I'm developing a socket application, which must must be to be robust to network failures.</p>
  
  <p>The application has 2 running threads, one waiting messages from the socket (a read() loop) and the other send messages to the socket  (a write() loop).</p>
  
  <p>I'm currently trying to use SO_KEEPALIVE to handle the network failures.
  It works ok if I'm only blocked on read(). A few seconds after the connection is lost (network cable removed), read() will fail with the message 'Connection timed out'.</p>
  
  <p>But, if I try to wrte() after the network is disconnected (and before the timeout ends), both write() and read() will block forever, without error.</p>
  
  <p>This is a stripped sample code which directs stdin/stdout to the socket. It listens on port 5656:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;pthread.h&gt;
  #include &lt;sys/types.h&gt; 
  #include &lt;sys/socket.h&gt;
  #include &lt;netinet/in.h&gt;
  #include &lt;netinet/tcp.h&gt;
  
  int socket_fd;
  
  void error(const char *msg) {
      perror(msg);
      exit(1);
  }
  
  //Read from stdin and write to socket
  void* write_daemon (void* _arg) {
      while (1) {
          char c;
          int ret = scanf("%c", &amp;c);
          if (ret &lt;= 0) error("read from stdin");
          int ret2 = write(socket_fd, &amp;c, sizeof(c));
          if (ret2 &lt;= 0) error("write to socket");
      }
      return NULL;
  }
  
  //Read from socket and write to stdout
  void* read_daemon (void* _arg) {
      while (1) {
          char c;
          int ret = read(socket_fd, &amp;c, sizeof(c));
          if (ret &lt;= 0) error("read from socket");
          int ret2 = printf("%c", c);
          if (ret2 &lt;= 0) error("write to stdout");
      }
      return NULL;
  }
  
  
  //Enable and configure KEEPALIVE - To detect network problems quickly
  void config_socket() {
      int enable_no_delay   = 1;
      int enable_keep_alive = 1;
      int keepalive_idle     =1; //Very short interval. Just for testing
      int keepalive_count    =1;
      int keepalive_interval =1;
      int result;
  
      //=&gt; http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#setsockopt
      result = setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &amp;enable_keep_alive, sizeof(int));
      if (result &lt; 0)
          error("SO_KEEPALIVE");
  
      result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPIDLE, &amp;keepalive_idle, sizeof(int));
      if (result &lt; 0) 
          error("TCP_KEEPIDLE");
  
      result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPINTVL, &amp;keepalive_interval, sizeof(int));
      if (result &lt; 0) 
          error("TCP_KEEPINTVL");
  
      result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPCNT, &amp;keepalive_count, sizeof(int));
      if (result &lt; 0) 
          error("TCP_KEEPCNT");
  }
  
  int main(int argc, char *argv[]) {
      //Create Server socket, bound to port 5656
      int listen_socket_fd;
      int tr=1;
      struct sockaddr_in serv_addr, cli_addr;
      socklen_t clilen = sizeof(cli_addr);
      pthread_t write_thread, read_thread;
  
      listen_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
      if (listen_socket_fd &lt; 0)
          error("socket()");
  
      if (setsockopt(listen_socket_fd,SOL_SOCKET,SO_REUSEADDR,&amp;tr,sizeof(int)) &lt; 0)
          error("SO_REUSEADDR");
  
      bzero((char *) &amp;serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = INADDR_ANY;
      serv_addr.sin_port = htons(5656);
      if (bind(listen_socket_fd, (struct sockaddr *) &amp;serv_addr, sizeof(serv_addr)) &lt; 0)
          error("bind()");
  
      //Wait for client socket
      listen(listen_socket_fd,5);
      socket_fd = accept(listen_socket_fd, (struct sockaddr *) &amp;cli_addr, &amp;clilen);
      config_socket();
      pthread_create(&amp;write_thread, NULL, write_daemon, NULL);
      pthread_create(&amp;read_thread , NULL, read_daemon , NULL);
      close(listen_socket_fd);
      pthread_exit(NULL);
  }
  </code></pre>
  
  <p>To reproduce the error, use telnet  5656.
  If will exit after a couple os seconds after the connection is lost, unless I try to write something in the terminal. In this case, it will block forever.</p>
  
  <p>So, the questions are: what's wrong? how to fix it? Are there other alternatives?</p>
  
  <p>Thanks!</p>
  
  <hr>
  
  <p>I've tried using Wireshark to inspect the network connection. If I don't call write(), I can see TCP keep-alive packages being sent and the connection is close after a few seconds. </p>
  
  <p>If, instead, I try to write(), it stops sending the Keep-Alive packets, and starts sending TCP retransmissions instead (It seems OK to me). The problem is, the time between the retransmissions grows bigger and bigger after each failure, and it seems to never give-up and close the socket.</p>
  
  <p>Is there a way to set the maximum number of retransmissions, or anything similar?
  Thanks</p>