Не удалось подключиться к сокету Linux - PullRequest
0 голосов
/ 12 декабря 2018

Я хочу создать сокет в Android с NDK, но иногда я получаю некоторые ошибки при подключении к серверу, и я могу убедиться, что сеть мобильного телефона пользователя доступна.Один случай - ошибка тайм-аута, а другой кажется, что соединение отказано, потому что я получаю следующие журналы, я думаю, что вторая ошибка - отказ в соединении, потому что ошибка от getsockopt равна 111, хотя strerror дает мне Операцию, которая сейчас выполняется, но адрес сервера действителен:

connect::socket error: Operation now in progress 
Or 
connect::error:111, Operation now in progress

Вот мой фрагмент кода:

bool connect(int sockfd, struct sockaddr *address, socklen_t address_len, int timeout) {
    int ret = 0;
    struct timeval tv;
    fd_set mask;

    // set socket non block
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    // use select to check socket connection
    ret = connect(sockfd, address, address_len);
    if (-1 == ret) {
        if (errno != EINPROGRESS) {
            perror("connect");
            inetConnectFailCode = errno;
            LOG(TAG.c_str(), "connect::errno != EINPROGRESS: %s", strerror(errno));
            return false;
        }
        LOG(TAG.c_str(), "connecting...\n");

        FD_ZERO(&mask);
        FD_SET(sockfd, &mask);
        tv.tv_sec = timeout;
        tv.tv_usec = 0;

        if (select(sockfd + 1, NULL, &mask, NULL, &tv) > 0) {
            int error = 0;
            socklen_t tmpLen = sizeof(int);
            int retopt = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &tmpLen);
            if (retopt != -1) {
                if (0 == error) {
                    LOG(TAG.c_str(), "has connect");
                    return true;
                } else {
                    //I get error here
                    LOG(TAG.c_str(), "connect::error:%d, %s", error, strerror(errno));
                    return false;
                }
            } else {
                LOG(TAG.c_str(), "connect::socket error:%d", error);
                return false;
            }
        } else {
            //timeout, and I get error here sometimes
            LOG(TAG.c_str(), "connect::socket error: %s", strerror(errno));
            return false;
        }
    }
    LOG(TAG.c_str(), "has connect");
    return true;
}

Эта проблема беспокоила меня долгое время, любой может оказать мне услугу,спасибо за продвижение.

1 Ответ

0 голосов
/ 12 декабря 2018

Отображается неправильное сообщение об ошибке.

Если 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 вместо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...