我试图提供connect()的超时。我搜索了四处,发现了几篇与此相关的文章。我已经编写了我认为应该工作的内容,但不幸的是,我没有从getsockopt()中报告错误。但是当我来写()它失败,错误107 - ENOTCONN。连接到套接字时似乎无法获取超时工作
几点。我正在Fedora 23上运行。connect()的文档表示,它应该返回失败,并返回EINPROGRESS的errno,但对于尚未完成的连接,但是我遇到了EAGAIN,因此我将其添加到了我的检查中。目前我的套接字服务器在listen()调用中将backlog设置为零。许多调用都成功了,但那些失败的调用都失败了,我在write()调用中提到了107-ENOTCONN。
我希望我只是失去了一些东西,但到目前为止无法弄清楚什么。
int domain_socket_send(const char* socket_name, unsigned char* buffer,
unsigned int length, unsigned int timeout)
{
struct sockaddr_un addr;
int fd = -1;
int result = 0;
// Create socket.
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
result = -1;
goto done;
}
if (timeout != 0)
{
// Enabled non-blocking.
int flags;
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
// Set socket name.
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
// Connect.
result = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
if (result == -1)
{
// If some error then we're done.
if ((errno != EINPROGRESS) && (errno != EAGAIN))
goto done;
fd_set write_set;
struct timeval tv;
// Set timeout.
tv.tv_sec = timeout/1000000;
tv.tv_usec = timeout % 1000000;
unsigned int iterations = 0;
while (1)
{
FD_ZERO(&write_set);
FD_SET(fd, &write_set);
result = select(fd + 1, NULL, &write_set, NULL, &tv);
if (result == -1)
goto done;
else if (result == 0)
{
result = -1;
errno = ETIMEDOUT;
goto done;
}
else
{
if (FD_ISSET(fd, &write_set))
{
socklen_t len;
int socket_error;
len = sizeof(socket_error);
// Get the result of the connect() call.
result = getsockopt(fd, SOL_SOCKET, SO_ERROR,
&socket_error, &len);
if (result == -1)
goto done;
// I think SO_ERROR will be zero for a successful
// result and errno otherwise.
if (socket_error != 0)
{
result = -1;
errno = socket_error;
goto done;
}
// Now that the socket is writable issue another connect.
result = connect(fd, (struct sockaddr*) &addr,
sizeof(addr));
if (result == 0)
{
if (iterations > 1)
{
printf("connect() succeeded on iteration %d\n",
iterations);
}
break;
}
else
{
if ((errno != EAGAIN) && (errno != EINPROGRESS))
{
int err = errno;
printf("second connect() failed, errno = %d\n",
errno);
errno = err;
goto done;
}
iterations++;
}
}
}
}
}
// If we put the socket in non-blocking mode then put it back
// to blocking mode.
if (timeout != 0)
{
// Turn off non-blocking.
int flags;
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
// Write buffer.
result = write(fd, buffer, length);
if (result == -1)
{
int err = errno;
printf("write() failed, errno = %d\n", err);
errno = err;
goto done;
}
done:
if (result == -1)
result = errno;
else
result = 0;
if (fd != -1)
{
shutdown(fd, SHUT_RDWR);
close(fd);
}
return result;
}
UPDATE 2016年4月5日:
我渐渐明白,也许我需要调用connect()多次,直到成功,毕竟这是非阻塞IO不是异步IO。就像在read()中遇到EAGAIN之后有数据需要读取时,必须再次调用read()。另外,我发现下面的SO问题:
Using select() for non-blocking sockets to connect always returns 1
其中EJP的回答说,你需要发出多个连接()的。另外,从书中引用EJP:
这似乎表明你需要发出多个连接()的。我修改了这个问题中的代码片段来调用connect(),直到它成功。我可能仍然需要对可能更新传递给select()的超时值进行更改,但那不是我的直接问题。
调用connect()多次似乎已经解决了我原来的问题,这是我调用write()时得到ENOTCONN,我猜是因为套接字没有连接。但是,您可以从代码中看到,我正在跟踪通过选择循环多少次,直到connect()成功。我已经看到了数以千计的数字。这让我担心我处于忙碌的等待循环中。为什么套接字可写,即使它不处于connect()将会成功的状态?调用connect()清除那个可写状态,并且由于某种原因它被操作系统重新设置,或者我真的处于繁忙的等待循环中?
感谢, 尼克
您确保'函数strncpy()'不砍掉从什么是作为'socket_name'传递什么? – alk
所示的代码是否适用于'timeout == 0',会在阻塞模式下表示? – alk
当超时时间为零时,代码没有问题。实际上,代码始于没有超时值。然后,我添加了timeout和“if(timeout!= 0)”语句,并且在select()之后添加了result = -1时的所有代码。 – nickdu