为 SO_RCVTIMEO 和 SO_SNDTIMEO 正确使用 getsockopt 和 setsockopt
Proper use of getsockopt and setsockopt for SO_RCVTIMEO and SO_SNDTIMEO
由于各种原因,我想在读取和写入服务器中的套接字时实现超时,但未能实现 运行,因此请您深入了解问题可能出在哪里。
为了设置读取和写入套接字的超时时间,我尝试使用函数 setsocketopt()
和 getsocketopt()
。但是,我一定是做错了什么,因为 return 值表明发生了问题并且错误输出 "Invalid argument"。奇怪的是,错误并不总是发生在第一次使用 setsocketopt()
和 getsocketopt()
时,这让我有点困惑。
以下服务器和客户端代码重现了我的问题(使用 gcc 编译)
服务器代码:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in saddr,caddr;
socklen_t clen;
int sock, csock, reuse = 1, ret=0;
socklen_t ntrcv, ntsnd;
struct timeval tout, tsnd, trcv;
// create a new stream socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create socket");
else {
// enable the socket to reuse the address
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) perror("failed allowing server socket to reuse address");
else {
// set up the server address
memset((char *) &saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(45454);
// bind socket to address
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) perror("failed to bind");
else {
// listen to the socket for connections
if (listen(sock,5) < 0) perror("failed to listen");
else {
clen = sizeof(caddr);
if ((csock = accept(sock, (struct sockaddr *) &caddr, &clen)) < 0) perror("failed to accept");
else {
tout.tv_sec=0;
tout.tv_usec=10000;
// check value of errno prior to setting timeout
perror("errno prior to timeout");
if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, &ntrcv) < 0) perror("2");
else if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, &ntsnd) < 0) perror("3");
else if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) perror("4");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) perror("5");
else {
printf ("all ok so far in server\n");
sleep(1);
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, ntrcv) < 0) perror("6");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, ntsnd) < 0) perror("7");
}
if (close(csock) < 0) perror("failed to close csock");
}
}
}
}
}
if (close(sock) < 0) perror("failed to close sock");
return ret;
}
客户代码:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in addr;
struct hostent *server;
int sock = 0;
// resolve server name
if (!(server = gethostbyname("127.0.0.1"))) perror("failed to resolve host");
else {
// prepare the server address
memset((char *) &addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
addr.sin_port = htons(45454);
// create a socket and connect to the server
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create client socket");
else {
if (connect(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) perror("failed to connect to server");
else {
printf("Connection is established will sleep now\n");
sleep(3);
printf("done sleeping\n");
close(sock);
printf("socket is closed\n");
}
}
}
return 0;
}
在本示例中,服务器中对 getsockopts()
的第二次调用失败,导致调用 perror("3")
。但是,如果我将这一行以及对 setsockopts()
的最后一次调用注释掉,那么对 getsockopts()
的第一次调用将失败(以前似乎有效)。
感谢任何对我哪里出错的见解
您没有将 ntsnd
和 ntrcv
初始化为 getsockopt
可用的缓冲区大小。因此,如果那里的随机值大于或等于所需的大小,调用就会成功。否则它将失败并返回 EINVAL
。来自手册页:
The arguments optval
and optlen
are used to access option values for setsockopt()
. For getsockopt()
they identify a buffer in which the value for the requested option(s) are to be returned. For getsockopt()
, optlen
is a value-result argument, initially containing the size of the buffer pointed to by optval
, and modified on return to indicate the actual size of the value returned. If no option value is to be supplied or returned, optval
may be NULL
.
要修复此问题,请将它们都初始化为 sizeof(struct timeval)
。
您需要将ntsnd
和ntrcv
变量分别设置为sizeof ntsnd
和sizeof ntrcv
,或者sizeof struct timeval
.
由于各种原因,我想在读取和写入服务器中的套接字时实现超时,但未能实现 运行,因此请您深入了解问题可能出在哪里。
为了设置读取和写入套接字的超时时间,我尝试使用函数 setsocketopt()
和 getsocketopt()
。但是,我一定是做错了什么,因为 return 值表明发生了问题并且错误输出 "Invalid argument"。奇怪的是,错误并不总是发生在第一次使用 setsocketopt()
和 getsocketopt()
时,这让我有点困惑。
以下服务器和客户端代码重现了我的问题(使用 gcc 编译)
服务器代码:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in saddr,caddr;
socklen_t clen;
int sock, csock, reuse = 1, ret=0;
socklen_t ntrcv, ntsnd;
struct timeval tout, tsnd, trcv;
// create a new stream socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create socket");
else {
// enable the socket to reuse the address
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) perror("failed allowing server socket to reuse address");
else {
// set up the server address
memset((char *) &saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(45454);
// bind socket to address
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) perror("failed to bind");
else {
// listen to the socket for connections
if (listen(sock,5) < 0) perror("failed to listen");
else {
clen = sizeof(caddr);
if ((csock = accept(sock, (struct sockaddr *) &caddr, &clen)) < 0) perror("failed to accept");
else {
tout.tv_sec=0;
tout.tv_usec=10000;
// check value of errno prior to setting timeout
perror("errno prior to timeout");
if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, &ntrcv) < 0) perror("2");
else if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, &ntsnd) < 0) perror("3");
else if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) perror("4");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) perror("5");
else {
printf ("all ok so far in server\n");
sleep(1);
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, ntrcv) < 0) perror("6");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, ntsnd) < 0) perror("7");
}
if (close(csock) < 0) perror("failed to close csock");
}
}
}
}
}
if (close(sock) < 0) perror("failed to close sock");
return ret;
}
客户代码:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in addr;
struct hostent *server;
int sock = 0;
// resolve server name
if (!(server = gethostbyname("127.0.0.1"))) perror("failed to resolve host");
else {
// prepare the server address
memset((char *) &addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
addr.sin_port = htons(45454);
// create a socket and connect to the server
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create client socket");
else {
if (connect(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) perror("failed to connect to server");
else {
printf("Connection is established will sleep now\n");
sleep(3);
printf("done sleeping\n");
close(sock);
printf("socket is closed\n");
}
}
}
return 0;
}
在本示例中,服务器中对 getsockopts()
的第二次调用失败,导致调用 perror("3")
。但是,如果我将这一行以及对 setsockopts()
的最后一次调用注释掉,那么对 getsockopts()
的第一次调用将失败(以前似乎有效)。
感谢任何对我哪里出错的见解
您没有将 ntsnd
和 ntrcv
初始化为 getsockopt
可用的缓冲区大小。因此,如果那里的随机值大于或等于所需的大小,调用就会成功。否则它将失败并返回 EINVAL
。来自手册页:
The arguments
optval
andoptlen
are used to access option values forsetsockopt()
. Forgetsockopt()
they identify a buffer in which the value for the requested option(s) are to be returned. Forgetsockopt()
,optlen
is a value-result argument, initially containing the size of the buffer pointed to byoptval
, and modified on return to indicate the actual size of the value returned. If no option value is to be supplied or returned,optval
may beNULL
.
要修复此问题,请将它们都初始化为 sizeof(struct timeval)
。
您需要将ntsnd
和ntrcv
变量分别设置为sizeof ntsnd
和sizeof ntrcv
,或者sizeof struct timeval
.