【问题标题】:TCP Connect With Invalid Connection Blocks PortTCP 连接无效的连接块端口
【发布时间】:2016-06-16 04:58:59
【问题描述】:

我正在用 c++ 编写一个 TCP 通信脚本,用于在我的计算机和 Aldebaran Nao 机器人之间进行通信。

总的来说,我的脚本可以正常工作。但是,我遇到的麻烦是,当我从客户端调用 connect 时(当服务器应用程序关闭或以太网连接被删除时)我收到一个错误,表明操作正在进行中。

但是,一旦重新启动服务器应用程序/重新连接以太网电缆,我仍然无法调用 connect 来成功重新建立连接。我仍然收到操作正在进行中的错误。

请注意,每当我的客户端确定无法建立连接时,套接字描述符都会在重新尝试连接之前关闭。这是我在客户端连接的代码:

如果有更多有用的信息,我很乐意提供。这个项目比较大,所以我不想在这里包含太多无关的信息。

 TCPStream* TCPConnector::connect(const char* serverIP, int port, int timeoutSec)
{
    if (timeoutSec == 0)
    {
        return connect(serverIP, port);
    }

    struct sockaddr_in address;

    // Store all zeros for address struct.
    memset(&address, 0, sizeof(address));

    // Configure address struct.
    address.sin_family = AF_INET;
    address.sin_port = htons(port); // Convert from host to TCP network byte order.
    inet_pton(PF_INET, serverIP, &(address.sin_addr)); // Convert IP address to network byte order.

    // Create a socket.  The socket signature is as follows: socket(int domain, int type, int protocol)
    int sd = socket(AF_INET, SOCK_STREAM, 0);

    int optval = 1;

    if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) == -1)
    {
        std::cout << "failed to set socket option" << std::endl;
    }

    // Set socket to be non-blocking.
    int arg;
    arg  = fcntl(sd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sd, F_SETFL, arg);

    // Connect with time limit.
    fd_set set;
    FD_ZERO(&set);    // Clear the set.
    FD_SET(sd, &set); // Add our file descriptor to the set.

    struct timeval timeout;
    timeout.tv_sec  = timeoutSec;
    timeout.tv_usec = 0;

    // If the connect call returns 0, then the connection was established.  Otherwise,
    // check if the three-way handshake is underway.
    if (::connect(sd, (struct sockaddr *)&address, sizeof(address)) < 0)
    {
        // If the handshake is underway.
        if (errno == EINPROGRESS)
        {
            std::cout << "handshake in progress" << std::endl;

            // Designate timeout period.
            int ret = select(sd + 1, NULL, &set, NULL, &timeout);

            std::cout << "return value from select : " << ret << std::endl;

            // Check if timeout or an error occurred.
            if (ret <= 0)
            {
                return NULL;
            }
            else
            {
                // Check if select returned 1 due to an error.
                int valopt;
                socklen_t len = sizeof(int);

                getsockopt(sd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);

                if (valopt)
                {
                    char * errorMessage = strerror( errno); // get string message from errn
                    std::string msg (errorMessage);
                    std::cout << msg << std::endl;


                    return NULL;
                }
            }  
        }
        else
        {
            return NULL;
        }
    }

    // Return socket to blocking mode
    arg = fcntl(sd, F_GETFL, NULL);
    arg &= (~O_NONBLOCK);
    fcntl(sd, F_SETFL, arg);

    // Create stream object.
    return new TCPStream(sd, &address);
}

【问题讨论】:

  • 它不会阻塞端口。您的代码将进入无限循环。
  • @EJP 你能解释一下导致无限循环的原因吗?

标签: c++ sockets tcp-ip


【解决方案1】:

您的套接字是非阻塞模式(您明确地这样做)。

因此,您的连接将立即返回“正在连接”。当套接字非阻塞时,您需要在此套接字上 poll 并等待它变得可读和/或可写 - 这意味着连接已完成(无论成功与否)。

在我看来,更好的选择是使用阻塞套接字——我认为你没有理由在这里使用非阻塞调用。

【讨论】:

  • 我合并了非阻塞套接字,以便我的客户端应用程序可以继续,即使无法连接。在将套接字设置为非阻塞后,我使用 select 在给定的超时时间内轮询响应。
  • 正确的方法,假设您不是为 Windows 编写的(使用重叠 io),但这对连接没有帮助。就像谢尔盖说的那样,您必须以一种或另一种方式等待连接完成或失败。另一方面,您似乎要去the same direction as boost::asio。在花费太多时间重新发明轮子之前,可能值得您花时间研究它。
  • @Austin,还有什么意义呢?您仍在等待连接结果。只需做一个阻塞套接字,就可以省去很多后顾之忧。
  • @SergeyA 因为我最终需要能够将控制权返回给调用应用程序。在建立连接或发生错误之前,块不会无限期地控制吗?
  • 另外,你们认为超时是问题所在吗?通常连接发生得很快,所以我不确定为什么在这种情况下建立连接需要更长的时间。
猜你喜欢
  • 2016-06-20
  • 1970-01-01
  • 1970-01-01
  • 2019-08-15
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 2014-01-14
  • 1970-01-01
相关资源
最近更新 更多