我正在客户端UNIX套接字上执行阻塞connect()调用。以下是代码示例:
// Create socket.
fds[i] = socket(AF_UNIX, SOCK_STREAM, 0);
if (fds[i] == -1)
{
result = -1;
goto done;
}
printf("generate_load thread, fds[%d]: %d\n", i, fds[i]);
// int flags = fcntl(fds[i], F_GETFL);
// fcntl(fds[i], F_SETFL, flags | O_NONBLOCK);
// If we have a timeout value we're only going to use that as
// a connect timeout. From looking at some source code, it
// appears the only way to timeout (correctly) a unix domain
// socket connect() call is to set the send timeout.
struct timeval existing_timeout;
if (timeout != 0)
{
socklen_t len = sizeof(existing_timeout);
getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &existing_timeout,
&len);
struct timeval tv;
tv.tv_sec = timeout / 1000000;
tv.tv_usec = timeout % 1000000;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
// Set socket name.
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
// @ indicates abstract name and abstract names begin with a NULL
// byte.
if (socket_name[0] == '@')
addr.sun_path[0] = '\0';
// Connect.
result = connect(fds[i], (struct sockaddr*) &addr, sizeof(addr));
if (result == -1)
{
printf("generate_load thread, failed connecting: %d\n", errno);
if (errno == EAGAIN)
errno = ETIMEDOUT;
goto done;
}
printf("generate_load thread, connected fds[%d]: %d\n", i, fds[i]);
// If we set a timeout then set it back to what it was.
if (timeout != 0)
{
setsockopt(fds[i], SOL_SOCKET, SO_SNDTIMEO, &existing_timeout,
sizeof(existing_timeout));
}
此代码一切正常,直到接受方(目前处于同一进程)由于文件描述符限制而失败。 accept()调用失败,错误为errno = 24(EMFILE)。我很好地得到错误,但为什么客户端没有看到错误?相反,客户端被阻止,永远不会返回。如您所见,我注释掉了将套接字置于非阻塞模式的行。我相信在非阻塞模式下我会遇到一些EAGAIN错误。
此外,当我达到文件描述符限制时,接受方似乎总是试图接受该套接字。我正在使用select()并等待侦听套接字准备好读取。当它是我做一个接受()。我可以理解得到第一个EMFILE错误,但我认为错误会被传回到connect()调用,这会导致代码突破其循环,因此不会再进行连接调用我原以为会导致接受方在select()调用中被阻止。
以下是听力方面的片段。下面的代码在while(1)循环中,首先调用select():
if (FD_ISSET(ti->listen_fd, &read_set) != 0)
{
printf("select thread, accepting socket\n");
int sock = accept(ti->listen_fd, NULL, NULL);
printf("select thread, accepted socket\n");
if (sock == -1)
{
printf("select thread, failed accepting socket: %d\n", errno);
if (error_threshold_met(&eti) == 0)
{
log_event(LOG_LEVEL_ERROR, "select thread, accept() "
"failed: %s", get_error_string(errno, error_string,
sizeof(error_string)));
}
}
代码似乎工作正常,直到我达到1024文件描述符限制。任何想法为什么它这样做?它应该是,我只是不明白它应该如何工作?
谢谢, 尼克
答案 0 :(得分:3)
connect()
和accept()
未互锁。您可以致电connect()
并让其返回,而根本不会致电accept()
。 TCP握手的服务器端部分在内核中独立于accept()
发生。 accept()
所做的就是从队列中选择一个传入连接并在其周围创建一个套接字,在队列为空时阻塞。由于FD耗尽,套接字创建部分失败,但实际连接已经建立。