所以在這里我設定了一個到服務器的 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);
}
}
}
uj5u.com熱心網友回復:
根據 select() 手冊頁:
select() returns the number of ready descriptors that are contained in
the descriptor sets, or -1 if an error occurred. If the time limit
expires, select() returns 0.
...所以如果 select() 回傳 0,那是因為在達到超時之前沒有完成任何 I/O 操作。
至于為什么沒有完成 I/O 操作:如果你在等待一個 TCP 連接完成,那么最可能的解釋是 TCP 連接還沒有完成(可能是因為網路速度慢、過載或損壞)服務器?)。
另一種(不太可能,但可能)解釋可能是您在 Windows 下運行程式,并且在 Windows 下,如果非阻塞 connect() 失敗,則通過在例外fd_set 中設定一個位來指示該失敗(即您將作為第四個引數傳遞給select(),就在超時引數之前)。在發布的代碼中,您為該引數傳遞了 NULL,這意味著在 Windows 下,您無法知道您的非阻塞 TCP 連接嘗試何時失敗。(在其他作業系統下,失敗的連接會導致套接字選擇為read-for-read和ready-for-write,這使得連接失敗更容易做出反應)
uj5u.com熱心網友回復:
我的理解是在 connect() 之后設定了非阻塞模式,以防它沒有立即與 getsockopt() 連接,指示 connect() 呼叫是否建立但由于某種原因,select 似乎回傳 0。是否必須延遲太小了?
非阻塞必須在socket(2)呼叫之后和呼叫之前設定connect(2),否則連接將被阻塞(未到達select()呼叫),直到 connect(2) 失敗。這通常超過兩分鐘,而這個技巧只需等待 5 秒。在連接呼叫中。
對于遠程站點的遠程連接,5 秒的延遲通常很小。在局域網中,如果您在 5 秒內沒有連接。那么這意味著有問題。
我敢打賭,出了點問題,您正在嘗試連接到不可用的套接字(不存在的主機,檢查服務器是否正在偵聽您嘗試連接的地址:埠),您忘記轉換進入網路位元組順序sockaddr_in結構中的某些欄位(這在您的代碼段中似??乎是正確的)或防火墻阻止您連接(這可能是事情)。您正在等待socketFd可用于寫入,這是正確的,因為如果未先連接連接,則不會(并阻止),因此顯然您正在正確地做事,因此某些地址拼寫錯誤或防火墻正在切斷對服務器的訪問。
無論哪種方式,超時select都不是錯誤,而只是超時。您正在使用的軟體正在考慮 5 秒的超時。在套接字中致命,因此您必須詢問開發人員或檢查您的網路連接。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/405590.html
標籤:
