0

Below is abstract code of something like a daemon. I need to check if my daemon is already here (if yes, I exit). Then I close the bound socket, do some forks (they will live after the daemon is restarted, so I don't want them having my bound socket).

int is_me_here()
{
  int sck = socket(AF_INET, SOCK_STREAM, 0), temp=1;
  struct sockaddr_in addr; 
  addr.sin_port = htons(1234);
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  bzero(addr.sin_zero, 8); 

  //setsockopt(sck,SOL_SOCKET,SO_REUSEADDR,&temp,sizeof(int)); //       #1 HERE?  
  if ((bind(sck, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in))) < 0){
    printf("cannot bind. My daemon is already here\n"); 
    return -1; 
  }
  close(sck); 
  return 0;
}

void foo(){
  int sck = socket(AF_INET, SOCK_STREAM, 0), temp=1, new_sockfd;
  struct sockaddr_in addr, cliaddr; 
  addr.sin_port = htons(1234);
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  bzero(addr.sin_zero, 8); 

  //setsockopt(sck,SOL_SOCKET,SO_REUSEADDR,&temp,sizeof(int)); //       #2 HERE? 
  if ((bind(sck, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in))) < 0){
    printf("WTF\n"); 
    return; 
  }
  listen(sck, 5);
  socklen_t lenaddr = sizeof(struct sockaddr_in); 
  while(1){
    if ((new_sockfd = accept(sck, (struct sockaddr *)(&cliaddr), &lenaddr)) == -1){
      sleep(1); 
      continue;
    }
    // ...
    close(new_sockfd); 
  }
}

int main()
{
  if (is_me_here() < 0)
    return 0; 
  //  some forks. Dont wanna them having the socket binded. 
  foo();  
  return 0;
}

As I know, the kernel keeps bound sockets in TIME_WAIT. So, I need to use SO_REUSEADDR to bind again after the forks. But, where is the right place to apply SO_REUSEADDR? Will the kernel keep my socket in TIME_WAIT after is_me_here()? I don't listen() or accept() something.

P.S. on my system, and some other systems, the code works fine wherever I set the SO_REUSEADDR. But I am afraid that some another system will give me an error in bind() in foo().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Rainbow
  • 29
  • 6
  • And when you tested the shown code, after setting `SO_REUSEADDR` as proposed, what results did you get? – Sam Varshavchik Jan 18 '23 at 14:38
  • @SamVarshavchik, that's main problem). Wherever I setsockopt, the daemon work without problem, but I am afraid of some problems with other systems(maybe there will be some errors) – Rainbow Jan 18 '23 at 14:45
  • And the exact reason you're afraid is what, exactly? Do you fully understand your textbook's or reference manual's explanation of this option? If you do, then you should be confident as to whether this is correct, or not. If you don't understand something you read about what `SO_REUSEADDR` is all about, and you are concerned that you're missing something, what, specifically, are you unsure about? – Sam Varshavchik Jan 18 '23 at 14:46
  • As an aside, this is not an idiomatic way of testing whether a server is alive. It'd be more normal to have a pid file or Unix socket in a well-known location. – Useless Jan 18 '23 at 19:40

3 Answers3

1

You have to set the flag to an int, pass the pointer and the size of the flag.

You can use SO_REUSEADDR on both UDP and TCP sockets, right after you create the socket.

    // reuse
    int reuse = 1;
    int result = setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse));
    if ( result < 0 ) {
        perror("ERROR SO_REUSEADDR:");
    }
Something Something
  • 3,999
  • 1
  • 6
  • 21
1

In general, you always want to set SO_REUSEADDR on a TCP listening socket.

If you don't set it, if your program shuts down and is then restarted within 1 or 2 minutes, you can get an "address already in use" error if there are still sockets in TIME_WAIT.

You'll still get an "address already in use" error if you attempt to start the program and another program is already listening on that port.

This also means you don't need to open an extra socket first just to check. Just do it once, and exit if the socket is already in use.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

Set it after successfully initialising the socket:

int sck = socket(AF_INET, SOCK_STREAM, 0)

These errors return -1 on failure, so you should for them.

errno = 0;
if ((sck = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
     perror("socket");
     /* We failed to open a socket, handle the error here */
}

Now set the option:

if (setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) == -1) {
    perror("setsockopt");
    /* Handle error here */
}

Now bind().

See also: How do SO_REUSEADDR and SO_REUSEPORT differ?

Harith
  • 4,663
  • 1
  • 5
  • 20