Sending and Receiving Timeouts with SO_RCVTIMEO and SO_SNDTIMEO

While looking at a sample grpc program the other day a user had mentioned that certain socket options were not being honored in nanos. This is not surprising and there are quite a few things like this nanos might not support today - you'd be surprised how much code works regardless. Essentially grpc was trying to set a socket option that specifies a sending or receiving timeout if something can't be sent or received within a given time. You can see the getsockopt warning messages when running the example:

eyberg@venus:~/gr/grpc-go/examples/helloworld$ ops run -p 50051 main
running local instance
booting /home/eyberg/.ops/images/main ...
[0.056378] en1: assigned 10.0.2.15
2026/01/10 23:18:50 server listening at [::]:50051
[0.064762] getsockopt unimplemented: fd 3, level 1, optname 20
[0.065235] getsockopt unimplemented: fd 3, level 1, optname 21
[2.041377] en1: assigned FE80::FC0C:1CFF:FE52:5B3
[2.983208] getsockopt unimplemented: fd 6, level 1, optname 20
[2.985640] getsockopt unimplemented: fd 6, level 1, optname 21
2026/01/10 23:18:53 Received: world

If you are using the nightly branch you'll now see that these warning messages go away. How do these socket options actually work in practice though? We can look at the following example which tries to send a msg to a local tcp listener and we set a timeout of 1 second if it fails to do so within that time. (I added the call to getsockopt so that you can see a similar message to the one that you'd see in the grpc example.)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define PORT 8080

#define SERVER_IP "127.0.0.1"

void error(const char *msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

int main() {
  struct timeval timeout;
  int sock = 0;
  struct sockaddr_in serv_addr;
  const char *message = "hello";
  int optval = 0;
  socklen_t optlen = sizeof(optval);

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    error("socket creation failed");
  }

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(PORT);

  timeout.tv_sec = 1;
  timeout.tv_usec = 0;

  if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)) < 0) {
    error("oh no");
  }

  if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &optval, &optlen) < 0) {
    error("getsockopt failed");
  } else {
    printf("SO_SNDTIMEO val: %d\n", optval);
  }

  if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
    error("bad address");
  }

  if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    error("connect failed");
  }

  if (send(sock, message, strlen(message), 0) < 0) {
    error("send failed");
  }

  printf("msg sent: \"%s\"\n", message);

  close(sock);

  return 0;
}

In this example we set the socket option before we even connect so the timeout will affect both the connect call and the send call. We can run a simple netcat listener like so and see that everything works fine.

nc -l 8080
eyberg@venus:~/snd$ ./main
SO_SNDTIMEO val: 1
msg sent: "hello"

Now let's make the application use the timeout. To simulate a slow network we can manipulate traffic on loopback to inject a 1500ms delay using tc:

sudo tc qdisc add dev lo root netem delay 1500ms

Now when we run our program we can see that it fails. This lets our program know that we are timing out because of network issues - in a real world program instead of exiting we'd perhaps try again later and continue on doing other work.

eyberg@venus:~/snd$ ./main
SO_SNDTIMEO val: 1
connect failed: Operation now in progress

If you want to toggle the delay back off simply delete it - don't forget to do this once done as well.

sudo tc qdisc del dev lo root

Socket options such as SO_RCVTIMEO and SO_SNDTIMEO allow frameworks like grapc to handle crappy network graefully.

Stop Deploying 50 Year Old Systems

Introducing the future cloud.

Ready for the future cloud?

Ready for the revolution in operating systems we've all been waiting for?

Schedule a Demo