套接字连接从分离的线程

时间:2016-04-14 21:31:34

标签: c++ sockets

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

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

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

#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;
    }
}

关闭定时器上的套接字并不是取消'connect'操作,强制它因错误而中止。我也试过shutdown(sfd, SHUT_RDWR);但没有成功......

我的方法无效吗?为什么不起作用?

如何强制connect从分离的线程中出错?

1 个答案:

答案 0 :(得分:4)

  

关闭定时器上的套接字并不是取消'connect'操作,强制它在出错时中止。

哇!你绝对不能那样做。当你关闭套接字时,没有办法知道线程在connect中实际被阻塞(而不是打算调用connect)。在另一个线程正在或可能正在使用它的情况下在一个线程中释放资源是灾难的一种方法。

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

  1. 线程即将调用connect,因此会安排超时。

  2. 超时到期,套接字关闭。

  3. 某个库中的一个线程会创建一个新的套接字,由于某种原因而使用它,获得相同的套接字描述符。

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

  5. 您有两种选择:

    1. 使用非阻止connect操作。

    2. 使用类似信号的内容来中断调用connect的线程。

    3. 但我不禁要问你为什么这么烦。为什么需要中止连接?如果在超时之前连接没有成功,你需要做其他事情,那就继续吧。