У меня возникают проблемы с использованием 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? У кого-нибудь есть другое предложение, как мне отправить сообщение нескольким клиентам?
Во-первых, я бы попытался разобраться с проблемой EINTR. connect () был прерван (в этом смысл EINTR) и больше не пытается, потому что вы используете дескриптор asynch.
Что я обычно делаю в таких обстоятельствах, так это повторяю попытку: я оборачиваю функцию (подключаюсь в этом случае) через некоторое время. Если соединение успешно, я вырываюсь из цикла. Если это не удается, я проверяю значение errno. Если это EINTR, я пытаюсь снова.
Имейте в виду, что есть другие значения errno, которые заслуживают повторной попытки (EWOULDBLOCK — одно из них)
Других решений пока нет …