简体   繁体   English

标准C ++ TCP套接字,使用std :: async时,连接失败并使用EINTR

[英]standard C++ TCP socket, connect fails with EINTR when using std::async

I am having trouble using the std::async to have tasks execute in parallel when the task involves a socket. 当任务涉及套接字时,我无法使用std :: async使任务并行执行。

My program is a simple TCP socket server written in standard C++ for Linux. 我的程序是用标准C ++ for Linux编写的简单TCP套接字服务器。 When a client connects, a dedicated port is opened and separate thread is started, so each client is serviced in their own thread. 当客户端连接时,将打开专用端口并启动单独的线程,因此每个客户端都在各自的线程中得到服务。

The client objects are contained in a map. 客户端对象包含在地图中。

I have a function to broadcast a message to all clients. 我具有向所有客户端广播消息的功能。 I originally wrote it like below: 我最初是这样写的:

//  ConnectedClient is an object representing a single client
//  ConnectedClient::SendMessageToClient opens a socket, connects, writes, reads response and then closes socket
//  broadcastMessage is the std::string to go out to all clients

//  iterate through the map of clients
map<string, ConnectedClient*>::iterator nextClient;
for ( nextClient = mConnectedClients.begin(); nextClient != mConnectedClients.end(); ++nextClient )
{
    printf("%s\n", nextClient->second->SendMessageToClient(broadcastMessage).c_str());

}   

I have tested this and it works with 3 clients at a time. 我已经对此进行了测试,并且一次可以与3个客户一起使用。 The message gets to all three clients (one at a time), and the response string is printed out three times in this loop. 该消息到达所有三个客户端(一次一个),并且在此循环中响应字符串被打印出三遍。 However, it is slow, because the message only goes out to one client at a time. 但是,它很慢,因为该消息一次仅发送给一个客户端。

In order to make it more efficient, I was hoping to take advantage of std::async to call the SendMessageToClient function for every client asynchronously. 为了提高效率,我希望利用std :: async异步地为每个客户端调用SendMessageToClient函数。 I rewrote the code above like this: 我这样重写了上面的代码:

vector<future<string>> futures;

//  iterate through the map of clients
map<string, ConnectedClient*>::iterator nextClient;
for ( nextClient = mConnectedClients.begin(); nextClient != mConnectedClients.end(); ++nextClient )
{   
    printf("start send\n"); 
    futures.push_back(async(launch::async, &ConnectedClient::SendMessageToClient, nextClient->second, broadcastMessage, wait));
    printf("end send\n");

}   

vector<future<string>>::iterator nextFuture;
for( nextFuture = futures.begin(); nextFuture != futures.end(); ++nextFuture )
{
    printf("start wait\n");
    nextFuture->wait();
    printf("end wait\n");
    printf("%s\n", nextFuture->get().c_str());
}

The code above functions as expected when there is only one client in the map. 当地图中只有一个客户端时,以上代码将按预期运行。 That you see "start send" quickly followed by "end send", quickly followed by "start wait" and then 3 seconds later (I have a three second sleep on the client response side to test this) you see the trace from the socket read function that the response comes in, and then you see "end wait" 您看到“开始发送”之后迅速出现“结束发送”,然后迅速出现“开始等待”,然后3秒钟后(我在客户端响应端有3秒钟的睡眠时间对此进行了测试),您看到了套接字的跟踪读取响应进入的函数,然后看到“结束等待”

The problem is that when there is more than one client in the map. 问题是,当地图中有多个客户端时。 In the part of the SendMessageToClient function that opens and connects to the socket, it fails in the code identified below: 在打开并连接到套接字的SendMessageToClient函数的一部分中,它在以下标识的代码中失败:

    //  connected client object has a pipe open back to the client for sending messages
int clientSocketFileDescriptor;
clientSocketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);


//  set the socket timeouts  
    //  this part using setsockopt is omitted for brevity

    //  host name
struct hostent *server;
server = gethostbyname(mIpAddressOfClient.c_str());

if (server == 0) 
{
   close(clientSocketFileDescriptor);
    return "";
}

//
struct sockaddr_in clientsListeningServerAddress;
memset(&clientsListeningServerAddress, 0, sizeof(struct sockaddr_in)); 

clientsListeningServerAddress.sin_family = AF_INET;
bcopy((char*)server->h_addr, (char*)&clientsListeningServerAddress.sin_addr.s_addr, server->h_length);
clientsListeningServerAddress.sin_port = htons(mPortNumberClientIsListeningOn);

    //  The connect function fails !!!
if ( connect(clientSocketFileDescriptor, (struct sockaddr *)&clientsListeningServerAddress, sizeof(clientsListeningServerAddress)) < 0 )
{
    //  print out error code
            printf("Connected client thread: fail to connect %d \n", errno);
    close(clientSocketFileDescriptor);
    return response;
}

The output reads: "Connected client thread: fail to connect 4". 输出为:“连接的客户端线程:无法连接4”。

I looked this error code up, it is explained thus: 我查看了此错误代码,因此对其进行了解释:

#define EINTR            4      /* Interrupted system call */

I searched around on the internet, all I found were some references to system calls being interrupted by signals. 我在互联网上四处搜寻,发现所有被信号中断的系统调用的引用。

Does anyone know why this works when I call my send message function one at a time, but it fails when the send message function is called using async? 有谁知道为什么一次调用一次我的发送消息功能时这种方法有效,但是当使用异步调用发送消息功能时却失败了吗? Does anyone have a different suggestion how I should send a message to multiple clients? 有人对我应该如何向多个客户端发送消息有不同的建议吗?

First, I would try to deal with the EINTR issue. 首先,我将尝试处理EINTR问题。 connect ( ) has been interrupted (this is the meaning of EINTR) and does not try again because you are using and asynch descriptor. connect()已被中断(这是EINTR的含义),并且不会再试一次,因为您正在使用andynch描述符。 What I usually do in such a circumstance is to retry: I wrap the function (connect in this case) in a while cycle. 在这种情况下,我通常要做的是重试:我会在一段时间内包装该函数(在这种情况下为连接)。 If connect succeeds I break out of the cycle. 如果连接成功,我将退出周期。 If it fails, I check the value of errno. 如果失败,则检查errno的值。 If it is EINTR I try again. 如果是EINTR,请重试。 Mind that there are other values of errno that deserve a retry (EWOULDBLOCK is one of them) 请注意,还有其他errno值值得重试(EWOULDBLOCK是其中之一)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM