正确使用SO_REUSEADDR的方法?



下面是类似守护进程的抽象代码。我需要检查我的守护进程是否已经在这里(如果是,我退出)。然后我关闭绑定的套接字,执行一些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 heren"); 
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("WTFn"); 
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中保留绑定套接字。因此,我需要使用SO_REUSEADDR在分叉后再次绑定。但是,在哪里应用SO_REUSEADDR是正确的呢?内核将保持我的套接字在is_me_here()之后的TIME_WAIT?我没有listen()accept()之类的。

注:在我的系统和其他一些系统上,只要我设置了SO_REUSEADDR,代码就可以正常工作。但是我担心其他的系统会在foo()bind()中给我一个错误。

一般情况下,您总是希望在TCP侦听套接字上设置SO_REUSEADDR

如果你没有设置,如果你的程序关闭,然后在1或2分钟内重新启动,你可以得到一个"已在使用"的地址。如果TIME_WAIT中仍然存在套接字,则会出现错误。

你仍然会得到一个"已经在使用的地址"。如果尝试启动程序,而另一个程序已经在该端口上侦听,则会出错。

这也意味着您不需要先打开一个额外的套接字来检查。只执行一次,如果套接字已在使用,则退出。

您必须将标志设置为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:");
}

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

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().

参见:SO_REUSEADDR和SO_REUSEPORT有什么不同?

相关内容

最新更新