【发布时间】:2022-01-05 20:01:50
【问题描述】:
所以我在这里建立了一个到服务器的 TCP 连接,并在循环中从应用程序调用,有时我最终会看到以下错误
select() timed out after 4 seconds - Operation now in progress
这意味着select 确实返回了0,这意味着它在 5 秒内超时,而没有观察到文件描述符上的任何活动。
我的理解是在connect() 之后设置非阻塞模式,以防它没有立即与getsockopt() 连接,表明connect() 调用是否确实建立但由于某种原因,select 似乎返回 0。它必须与延迟太小?
int InitializeSocket(int sockType, int protocol, long timeout)
{
int socketFd = socket(AF_INET, sockType, protocol);
if (socketFd < 0)
{
perror ("Failed to create a client socket of type %d", sockType);
return -1;
}
if (timeout > 0)
{
struct timeval sockTimeout = {.tv_sec = timeout, .tv_usec = 0};
// setting the receive timeout
if (setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &sockTimeout, sizeof(sockTimeout)) < 0)
{
perror ("Failed to set the RX timeout");
return -1;
}
// setting the send timeout
if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, &sockTimeout, sizeof(sockTimeout)) < 0)
{
perror ("Failed to set the TX timeout");
return -1;
}
}
return socketFd;
}
void OpenTcpConnection(int serverTimeout, int port, const char *ipAddr)
{
struct sockaddr_in *address
int socketFd = InitializeSocket(SOCK_STREAM, 0, serverTimeout);
if (socketFd == -1)
{
return -1;
}
address->sin_family = AF_INET;
address->sin_port = htons(port);
address->sin_addr.s_addr = inet_addr(ipAddr);
memset(address->sin_zero, '\0', sizeof(address->sin_zero));
// get the existing file flags
long arg = 0;
if( (arg = fcntl(socketFd, F_GETFL, NULL)) < 0)
{
perror ("Failed to get file status flags");
exit(0);
}
// set the socket to nonblocking mode
arg |= O_NONBLOCK;
if( fcntl(socketFd, F_SETFL, arg) < 0)
{
perror ("Failed to set to nonblocking mode");
return -1;
}
// connect to the server
int res = connect(socketFd, (struct sockaddr *) &address, sizeof(address));
fd_set fdset;
struct timeval tv;
long selectTimeout = 4; // connect() timeout
if (res < 0)
{
// the socket is nonblocking & the connection cannot be completed immediately
if (errno == EINPROGRESS)
{
do
{
tv.tv_sec = selectTimeout;
tv.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(socketFd, &fdset);
res = select(socketFd+1, NULL, &fdset, NULL, &tv);
if (res < 0 && errno != EINTR)
{
perror ("Failed to monitor socket FD %d", socketFd);
return -1;
}
else if (res > 0)
{
int so_error;
socklen_t len = sizeof so_error;
int valopt;
// check whether connect() completed successfully
if (getsockopt(socketFd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len) < 0)
{
perror ("Error in getsockopt");
return -1;
}
if (valopt)
{
perror ("Error in delayed connection");
return -1;
}
break;
}
else
{
perror ("select() timed out after %ld seconds", selectTimeout); // ERROR HERE !!!
return -1;
}
} while(1);
}
}
}
【问题讨论】:
-
请注意,在
select()返回除-1 以外的任何值之后调用perror()是没有用的或不合适的。在任何其他情况下,perror()将报告的errno的值不能反映select()返回的原因。 -
请注意,您将
EINTR视为超时而不是重试。将if (res < 0 && errno != EINTR)更改为if (res < 0) { if (errno != EINTR) { ... } }
标签: c sockets tcp network-programming embedded-linux