【问题标题】:Socket connect abort from a detached thread套接字连接从分离的线程中止
【发布时间】:2016-04-15 05:49:12
【问题描述】:

我正在构建一个套接字客户端,我需要在其中实现连接、读取、写入超时以及协议本身的超时(缺少答案等)。

我正在考虑在一个分离线程中使用一个简单的计时器,该计时器将在每个事务上启动,然后在事务完成时取消。同样的方法将用于使用不同超时的协议控制。

为了测试我做了以下简单的代码:

#include <string>
#include <sstream>
#include <map>
#include <iostream>
#include <cstring>
#include <thread>

#ifdef _WIN32
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#endif

#include <stdio.h>
#include <stdlib.h>

bool timerOn = false;
int currentSocket = 0;


void Timer(int seconds)
{
    int tick = seconds;

    while (tick > 0)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        tick--;
    }

    if (timerOn)
        close(currentSocket);
}

void StartTimer(int seconds)
{
    timerOn = true;
    std::thread t(&Timer, seconds);
    t.detach();
}

void StopTimer()
{
    timerOn = false;
}


void Connect(std::string address, int port)
{
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    struct addrinfo *rp = NULL;
    int sfd, s;

    std::memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_family = AF_UNSPEC;        /* Allow IPV4 or IPV6 */
    hints.ai_socktype = SOCK_STREAM;    
    hints.ai_flags = 0;
    hints.ai_protocol = 0;              

    std::string portStr;
    portStr = std::to_string(port);

    s = getaddrinfo(address.c_str(), portStr.c_str(), &hints, &result);

    if (s != 0)
    {
        std::stringstream ss;
        ss << "Cannot resolve hostname " << address << gai_strerror(s);
        throw std::runtime_error(ss.str());
    }

    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);

        if (sfd == -1)
            continue;

        StartTimer(10);
        int sts = connect(sfd, rp->ai_addr, rp->ai_addrlen);
        StopTimer();

        if (sts == 0)
            break;

        close(sfd);
    }

    freeaddrinfo(result); /* Object no longer needed */

    if (rp == NULL)
    {
        std::stringstream ss;
        ss << "Cannot find server address at " << address << " port " << port;
        throw std::runtime_error(ss.str());
    }

    currentSocket = sfd;
}


int main()
{
    try
    {
        Connect("192.168.0.187", 9090);
        std::cout << "Connected to server. Congrats!!!" << std::endl;
    }
    catch (std::exception& ex)
    {
        std::cout << "Error connecting to server. Aborting." << std::endl;
        std::cout << ex.what() << std::endl;
    }
}

在计时器上关闭套接字不会取消“连接”操作,而是强制它因错误而中止。我也试过shutdown(sfd, SHUT_RDWR);,但没有成功...

我的方法无效吗?为什么它不工作?

如何强制connect 在分离线程出错时中止?

【问题讨论】:

  • 并非所有平台都允许通过简单地关闭套接字来中断connect()
  • @RemyLebeau 我知道没有。有时可能会碰巧发生,但实际上无法保证。 (仅使用常规的socketconnectclose API,实际上是无法完成的。您需要具有附加功能的 API,例如原子“连接然后解锁”功能。)
  • @DavidSchwartz Windows 允许通过关闭套接字来中止connect()
  • @RemyLebeau 使用什么 API?使用调试 API 检查另一个线程的堆栈以确认它在connect 中被阻止?你当然不能只用closeclosesocketconnect
  • @DavidSchwartz 我认为他的意思是如果线程 A 在 connect() 中被阻塞并且线程 B 在同一个套接字句柄上调用 closesocket(),那么 connect() 将失败并且线程 A 将解除阻塞.

标签: c++ sockets


【解决方案1】:

在计时器上关闭套接字不会取消“连接”操作,而是强制它以错误中止。

哇!你绝对不能那样做。当您关闭套接字时,无法知道线程实际上在connect 中被阻塞(而不是即将调用connect)。在一个线程中释放资源,而另一个线程正在使用或可能正在使用它是灾难的根源。

想象一下会发生这种情况:

  1. 一个线程即将调用connect,所以它安排了一个超时。

  2. 超时,socket关闭。

  3. 某个库中的线程出于某种原因创建了一个新套接字以供使用,并获得相同的套接字描述符。

  4. 即将调用connect的线程终于被调度并调用connect——连接库的套接字!灾难。

你有两个选择:

  1. 使用非阻塞connect 操作。

  2. 使用信号之类的东西来中断调用connect的线程。

但我想知道你为什么要打扰。为什么需要中止连接?如果在超时前连接没有成功,你需要做其他事情,那就继续做吧。

【讨论】:

  • "你为什么要打扰" - 因为没有超时,connect() 可能需要很长时间才能退出并出现错误。根据到达目的地所需的跳数,答案可能需要一段时间才能发现一种或另一种方式。与当今现代网络一样快,它们仍然不是即时的。如果您在超时后不中止它,它可能稍后还会回来并说“哦,是的,我终于成功了”,但您已经继续前进了。
  • @RemyLebeau 那又怎样?为什么这是个问题?即使你中止它,它仍然可以成功——事实上,它可能已经成功了,你只是没有收到最后的数据包这样说(或者对方还没有收到它,但不可撤销地会收到它)然后发送您收到的响应,完成连接尝试)。因此,对于中止已经(或以后)成功的连接的可能性,您无能为力。
  • 即使在较低级别成功,较高级别的套接字也将被中止/关闭,因此任何后续流量都应失败并返回重置响应。
  • @RemyLebeau 如果accept 成功完成但超时已过期,您可以通过立即关闭套接字(无需读取任何内容)轻松获得该行为。无论你做什么,你都无法避免对方看到正常的收盘价。
  • 所以,看来这种方法是无效的。我将按照建议重写所有内容以使用非阻塞connect
猜你喜欢
  • 1970-01-01
  • 2011-12-10
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-26
  • 2011-04-14
  • 2014-10-31
相关资源
最近更新 更多