使用 setsockopt() 指定超时选项会导致随后的侦听错误
Specifying timeout option with setsockopt() results in subsequent listen error
现在,我正在尝试使用以下代码通过 setsockopt()
指定选项:
// bind socket
// Use setsockopt() function to make sure the port is not in use
int yes = 1;
setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
setsockopt(TCPSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
status = bind(TCPSocket, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status == -1) std::cout << "bind error" << std::endl ;
// listen for connections
status = listen(TCPSocket, 5);
if (status == -1) std::cout << "listen error" << std::endl ;
int new_sd;
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
new_sd = accept(TCPSocket, (struct sockaddr *)&their_addr, &addr_size);
if (new_sd == -1) std::cout << "listen error" << std::endl ;
注意tv
是一个已经指定的时间间隔。
当我只进行第一个 setsockopt()
调用时,一切正常。但是,加上第二个(没有 return 任何错误),我遇到了代码中指定的第二个 "listen error"。我不确定为什么设置超时值会影响这一点,有人可以解释一下吗?
我不认为指定的代码是我的功劳;它是根据此处教程中提供的代码修改的:http://codebase.eu/tutorial/linux-socket-programming-c/
如果您看到 TCP 状态图 like this one you see there's a state called TIME_WAIT
when actively closing a socket. This state can take some time before it ends, up to four minutes according to RFC793。
当套接字处于 TIME_WAIT
时,您不能使用与处于等待状态的套接字相同的地址-端口对绑定到接口。在套接字上设置 SO_REUSEADDR
标志可以让其他套接字在当前套接字(设置了标志)处于 TIME_WAIT
状态时绑定到该地址。
SO_REUSEADDR
选项对服务器(被动、侦听)套接字最有用。
至于你的问题,每次调用 setsockopt
check what it returns, and if it's -1
then you check errno
to see what went wrong. You can use perror
or strerror
打印或获取错误的可打印字符串后,如
if (setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
{
std::cerr << "Error setting the SO_REUSEADDR: " << strerror(errno) << '\n';
// Do something appropriate
}
Joachim 的解决方案很好地回答了我最初的问题并解释了 setsockopt()。在意识到问题在代码的更下方之后回答我自己的问题,超时会影响服务器能够侦听端口。假设超时只有 10 毫秒,必须启动服务器,然后是客户端,并且必须在那个时间内建立连接。这在我的案例中没有发生,因此导致了错误。
现在,我正在尝试使用以下代码通过 setsockopt()
指定选项:
// bind socket
// Use setsockopt() function to make sure the port is not in use
int yes = 1;
setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
setsockopt(TCPSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
status = bind(TCPSocket, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status == -1) std::cout << "bind error" << std::endl ;
// listen for connections
status = listen(TCPSocket, 5);
if (status == -1) std::cout << "listen error" << std::endl ;
int new_sd;
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
new_sd = accept(TCPSocket, (struct sockaddr *)&their_addr, &addr_size);
if (new_sd == -1) std::cout << "listen error" << std::endl ;
注意tv
是一个已经指定的时间间隔。
当我只进行第一个 setsockopt()
调用时,一切正常。但是,加上第二个(没有 return 任何错误),我遇到了代码中指定的第二个 "listen error"。我不确定为什么设置超时值会影响这一点,有人可以解释一下吗?
我不认为指定的代码是我的功劳;它是根据此处教程中提供的代码修改的:http://codebase.eu/tutorial/linux-socket-programming-c/
如果您看到 TCP 状态图 like this one you see there's a state called TIME_WAIT
when actively closing a socket. This state can take some time before it ends, up to four minutes according to RFC793。
当套接字处于 TIME_WAIT
时,您不能使用与处于等待状态的套接字相同的地址-端口对绑定到接口。在套接字上设置 SO_REUSEADDR
标志可以让其他套接字在当前套接字(设置了标志)处于 TIME_WAIT
状态时绑定到该地址。
SO_REUSEADDR
选项对服务器(被动、侦听)套接字最有用。
至于你的问题,每次调用 setsockopt
check what it returns, and if it's -1
then you check errno
to see what went wrong. You can use perror
or strerror
打印或获取错误的可打印字符串后,如
if (setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
{
std::cerr << "Error setting the SO_REUSEADDR: " << strerror(errno) << '\n';
// Do something appropriate
}
Joachim 的解决方案很好地回答了我最初的问题并解释了 setsockopt()。在意识到问题在代码的更下方之后回答我自己的问题,超时会影响服务器能够侦听端口。假设超时只有 10 毫秒,必须启动服务器,然后是客户端,并且必须在那个时间内建立连接。这在我的案例中没有发生,因此导致了错误。