Android中的Linux套接字连接失败

时间:2018-12-12 03:07:12

标签: android linux sockets

我想在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 :(得分:2)

您正在显示错误的错误消息。

如果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,则无论实际返回什么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错误。

如果防火墙阻止了您的连接,则可能还会发生两种错误。

更新:根据我发布的an earlier answer的评论,如果积压已满,只有Windows服务器会导致ECONNREFUSED。 *由九台服务器代替ETIMEDOUT