我必须实现应用程序健康检查机制,虽然我取得了成功,但我使用非阻塞套接字和 select
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
int connect_tout(char * hostname1, int port, int timeoutval)
{
char *hostname = hostname1; /* pointer to name of server */
struct sockaddr_in saddr; /* socket address */
int s, i;
fd_set fd_r, fd_w;
struct timeval timeout;
int flags;
timeout.tv_sec = timeoutval;
timeout.tv_usec = 0;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(hostname1);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/* set the socket fd to non-blocking mode */
fcntl(s, F_SETFL, (flags = fcntl(s, F_GETFL)) | O_NONBLOCK);
connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
FD_ZERO(&fd_r);
FD_ZERO(&fd_w);
FD_SET(s, &fd_r);
FD_SET(s, &fd_w);
/* timeout durring connect() ?? */
select(s+1, &fd_r, &fd_w, NULL, &timeout);
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
printf("Conect TIMEOUT \n");
close(s);
return errno;
}
i = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if(i)
{
printf("Conect failed errno:%d\n",errno);
perror("connect:");
close(s);
return errno;
}
else
{
printf("Connect passed and OK \n");
close(s);
return 1;
}
close(s);
return 1;
}
int main (int argc, char *argv[])
{
int ret;
if (argc < 3)
{
printf("Usage: %s [host] [port] [timout]\n", argv[0]);
exit(1);
}
char *hostname = argv[1]; /* pointer to name of server <IP address>*/
connect_tout(hostname, atoi(argv[2]), atoi(argv[3]));
return 0;
}
但是当我的代码运行机器的 fd 使用率非常高时,我的问题就来了。注意:在我的系统中一次打开多个 fds 是常见的行为。那么这 block 每次都失败
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
close(s);
return errno;
printf("Conect TIMEOUT\n");
}
正如我在这样的环境中所说的那样,它通过说 TIMEOUT
失败了,我想知道为什么 select
没有这么快确定就绪的描述符而失败,而且每次都是这样。 FD_ISSET()
是否也有疑问?
P S :当系统在正常数量的 fds 下时,这运行良好。对不起,我刚刚在这里粘贴了我的示例工作代码的错误程序。稍后我会检查错误
最佳答案
对于非阻塞的 connect()
用法,您在收到可写通知后不会再次调用它。相反,您应该使用带有 SO_ERROR
选项的 getsockopt()
检查套接字的错误状态。
您没有检查任何调用的返回值,这使得您的代码无法真正正确地确定任何失败。请注意,如果传入的超时本身为 0,则不检查大小写,这将导致 select()
立即返回套接字的瞬时状态。请注意,套接字 API 未记录检查连接套接字的可读通知。
int s = socket(PF_INET, SOCK_STREAM, 0);
assert(!(s < 0));
int r = fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
assert(r == 0);
r = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (r < 0) {
if (errno == EINPROGRESS) {
FD_ZERO(&fd_w);
FD_SET(s, &fd_w);
r = select(s+1, NULL, &fd_w, NULL, NULL);
if (r < 0) {
perror("select");
abort();
}
assert(r == 1);
assert(FD_ISSET(s, &fd_w));
int erropt = -1;
socklen_t errlen = sizeof(erropt);
r = getsockopt(s, SOL_SOCKET, SO_ERROR, &erropt, &errlen);
assert(r == 0);
if (erropt != 0) {
errno = erropt;
perror("connect[after select]");
abort();
}
/* connect succeeded asynchronously */
} else {
perror("connect[direct call]");
abort();
}
} else {
/* connect succeeded synchronously */
}
关于c - select() 在 non_blocking 手动超时 connect() 调用的繁重条件下失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22967967/