我必须实现一个应用程序健康检查机制,我在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("ALIVEn");
}
else
{
printf("Conect TIMEOUT n");
close(s);
return errno;
}
i = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if(i)
{
printf("Conect failed errno:%dn",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使用率下时,我的问题就来了。注意:在我的系统中一次打开多个fd是常见的行为。那么每次都会失败
if(FD_ISSET(s, &fd_w))
{
printf("ALIVEn");
}
else
{
close(s);
return errno;
printf("Conect TIMEOUTn");
}
正如我所说,在这样的环境中,它通过说TIMEOUT
而失败,我想知道为什么select
没有这么快确定准备好的描述符而失败,这是每一次。FD_ISSET()
是否也可能受到质疑
PS:当系统处于正常数量的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 */
}