Отображается неправильное сообщение об ошибке.
Если getsockopt(SO_ERROR)
не удается, вы выводите error
, даже если оно не имеет допустимого значения.Но, что более важно, если getsockopt(SO_ERROR)
успешно, но error
не равно 0, вы передаете errno
в strerror()
вместо передачи error
.errno
по-прежнему EINPROGRESS
от первоначального неудачного вызова connect()
, поэтому ваше сообщение об ошибке гласит "Operation now in progress"
.Ошибка 111 - ECONNREFUSED
, которая вместо этого будет "Connection refused"
.
Кроме того, если select()
возвращает <= 0, вы выводите <code>strerror(errno) независимо от того, что на самом деле select()
вернуло.errno
действительно только в том случае, если select()
возвращает -1.Если select()
возвращает 0 вместо этого, не гарантируется обновление errno
.
Вместо этого попробуйте что-то вроде этого:
bool connect(int sockfd, struct sockaddr *address, socklen_t address_len, int timeout) {
// set socket non block
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
// use select to check socket connection
int ret = connect(sockfd, address, address_len);
if (-1 == ret) {
int error = errno;
if (EINPROGRESS == error) {
LOG(TAG.c_str(), "connecting...\n");
fd_set mask;
struct timeval tv;
FD_ZERO(&mask);
FD_SET(sockfd, &mask);
tv.tv_sec = timeout;
tv.tv_usec = 0;
ret = select(sockfd + 1, NULL, &mask, NULL, &tv);
if (0 < ret) {
socklen_t tmpLen = sizeof(int);
ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &tmpLen);
if (-1 == ret) {
error = errno;
}
} else if (0 == ret) {
error = ETIMEDOUT;
} else {
error = errno;
}
}
if (0 != error) {
inetConnectFailCode = error;
LOG(TAG.c_str(), "connect::error:%d, %s", error, strerror(error));
return false;
}
}
LOG(TAG.c_str(), "has connect");
return true;
}
С учетом сказанного вы можете получить ECONNREFUSED
ошибка, если серверный компьютер доступен и получает ваш запрос на соединение, но активно отклоняет его, потому что он не может принять ваше соединение в то время, либо потому что:
Нет способа определить, что именно так, поэтому вы будетепросто повторите попытку подключения позднее, предпочтительно после небольшого интервала времени (скажем, по крайней мере, 5 секунд, например).Сбой соединения, если несколько попыток подряд не удаются.
Вместо этого вы можете получить ошибку ETIMEDOUT
, если серверный сервер доступен и прослушивает, но не завершает трехстороннее рукопожатие до истечения времени ожидания.
Любая ошибка также может произойти, если брандмауэр блокирует ваше соединение.
Обновление: в соответствии с комментарием к более ранний ответ Я отправил, толькоСерверы Windows вызывают ECONNREFUSED
, если резерв заполнен.* Серверы Nix вызывают ETIMEDOUT
вместо.