2013-08-22 5 views
0

У меня возникли проблемы с использованием std :: async, чтобы задачи выполнялись параллельно, когда задача связана с сокетом.стандартный C++ TCP-сокет, сбой при соединении с EINTR при использовании 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()); 
} 

Код выше функции, как ожидается, когда есть только один клиент на карте. То, что вы быстро видите «начать отправку», за которым следует «конец отправки», быстро следует «начать ждать», а затем через 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; 
} 

Выхода гласит: «Connected клиентского потока: не удается подключиться 4».

Я смотрел этот код ошибки вверх, это объясняется следующим образом:

#define EINTR   4  /* Interrupted system call */ 

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

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

ответ

0

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