下面是类似守护进程的抽象代码。我需要检查我的守护进程是否已经在这里(如果是,我退出)。然后我关闭绑定的套接字,执行一些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有什么不同?