C语言 使用SO_REUSEADDR的正确方法?

x33g5p2x  于 2023-01-20  发布在  其他
关注(0)|答案(3)|浏览(213)

下面是一个类似守护进程的抽象代码。我需要检查我的守护进程是否已经在这里了(如果是,我退出)。然后我关闭绑定的套接字,做一些fork(它们在守护进程重新启动后仍然存在,所以我不希望它们拥有我的绑定套接字)。

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;
}

据我所知,内核将绑定套接字保存在TIME_WAIT中。因此,我需要在fork之后再次使用SO_REUSEADDR进行绑定。但是,应用SO_REUSEADDR的正确位置在哪里?内核将在is_me_here()之后将套接字保存在TIME_WAIT中吗?我不知道listen()accept()之类的。
在我的系统和其他一些系统上,无论我在哪里设置SO_REUSEADDR,代码都能正常工作。但是我担心其他一些系统会在foo()中给予我一个bind()错误。

a7qyws3x

a7qyws3x1#

通常,您总是希望在TCP侦听套接字上设置SO_REUSEADDR
如果您没有设置它,如果您的程序关闭,然后在1或2分钟内重新启动,您可以得到一个“地址已在使用中”错误,如果仍然有套接字在TIME_WAIT。
如果您试图启动该程序,而另一个程序已经在侦听该端口,您仍然会收到“地址已在使用”错误。
这也意味着你不需要为了检查而打开一个额外的套接字,只需要打开一次,如果套接字已经被使用,就退出。

mnemlml8

mnemlml82#

你必须把标志设置为int型,传递指针和标志的大小。
您可以在UDP和TCP套接字上使用SO_REUSEADDR,就在您创建套接字之后。

// reuse
    int reuse = 1;
    int result = setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse));
    if ( result < 0 ) {
        perror("ERROR SO_REUSEADDR:");
    }
kknvjkwl

kknvjkwl3#

成功初始化套接字后设置它:

int sck = socket(AF_INET, SOCK_STREAM, 0)

这些错误在失败时返回-1,因此您应该为它们设置。

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

现在设置选项:

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

现在bind()
另请参阅:How do SO_REUSEADDR and SO_REUSEPORT differ?

相关问题