Стандартный C++ TCP-сокет, происходит сбой соединения с EINTR при использовании std::async

У меня возникают проблемы с использованием std::async, чтобы задачи выполнялись параллельно, когда задача включает сокет.

Моя программа представляет собой простой сервер сокетов TCP, написанный на стандартном C++ для Linux. Когда клиент подключается, открывается выделенный порт и запускается отдельный поток, поэтому каждый клиент обслуживается в своем собственном потоке.

Объекты клиента содержатся в карте.

У меня есть функция для передачи сообщения всем клиентам. Я первоначально написал это как ниже:

//  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());

}   

Я проверил это, и он работает с 3 клиентами одновременно. Сообщение попадает ко всем трем клиентам (по одному за раз), и строка ответа выводится три раза в этом цикле. Однако это медленно, потому что сообщение отправляется только одному клиенту за раз.

Чтобы сделать его более эффективным, я надеялся воспользоваться преимуществом std::async для вызова функции SendMessageToClient для каждого клиента асинхронно. Я переписал приведенный выше код так:

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());
}

Приведенный выше код работает, как и ожидалось, когда на карте только один клиент. То, что вы видите "start send", за которым быстро следует "end send", за которым следует "start wait", а затем через 3 секунды (у меня три секунды ожидания на стороне ответа клиента, чтобы проверить это), вы видите трассировку из сокета прочитайте функцию, что ответ приходит, и затем вы видите "конец ожидания"

Проблема в том, что когда на карте больше одного клиента. В части функции 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;
}

Вывод гласит: "Подключенный поток клиента: не удается подключиться 4".

Я посмотрел этот код ошибки, это объясняется так:

#define EINTR            4      /* Interrupted system call */

Я искал в Интернете, все, что я нашел, было несколько ссылок на системные вызовы, прерываемые сигналами.

Кто-нибудь знает, почему это работает, когда я вызываю функцию отправки сообщения по одному, но происходит сбой, когда функция отправки сообщения вызывается с использованием async? У кого-нибудь есть другое предложение, как мне отправить сообщение нескольким клиентам?

1 ответ

Во-первых, я бы попытался разобраться с проблемой EINTR. connect () был прерван (это значение EINTR) и больше не пытается, потому что вы используете дескриптор asynch. Что я обычно делаю в таких обстоятельствах, так это повторяю попытку: я оборачиваю функцию (подключаюсь в этом случае) через некоторое время. Если соединение успешно, я вырываюсь из цикла. Если это не удается, я проверяю значение errno. Если это EINTR, я пытаюсь снова. Имейте в виду, что существуют другие значения errno, которые заслуживают повторной попытки (EWOULDBLOCK - одно из них)

Другие вопросы по тегам