2013-08-22 6 views
0

std :: async를 사용하여 작업에 소켓이 관련된 경우 작업을 병렬로 실행하는 데 문제가 있습니다.표준 C++ TCP 소켓, std :: async 사용할 때 EINTR로 연결이 실패합니다.

내 프로그램은 Linux 용 표준 C++로 작성된 간단한 TCP 소켓 서버입니다. 클라이언트가 연결되면 전용 포트가 열리고 별도의 스레드가 시작되므로 각 클라이언트는 자체 스레드에서 서비스됩니다.

클라이언트 개체는 맵에 포함되어 있습니다.

모든 클라이언트에게 메시지를 브로드 캐스팅하는 기능이 있습니다. 나는 원래 다음과 같이 썼다 :

// 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 명의 클라이언트와 동시에 작동한다. 메시지는 세 개의 클라이언트 (한 번에 하나씩)에 도착하고 응답 문자열은이 루프에서 세 번 출력됩니다. 그러나 메시지가 한 번에 하나의 클라이언트에만 전달되기 때문에 속도가 느립니다.

더 효율적으로하기 위해 모든 클라이언트에 대해 SendMessageToClient 함수를 비동기 적으로 호출하기 위해 std :: async를 활용하고 싶습니다. 위의 코드를 다음과 같이 다시 작성했습니다.

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 초 후에 (나는 이것을 테스트하기 위해 클라이언트 응답 측에서 3 초 잠을 자는데) 당신은 소켓에서 트레이스를 보게된다. 응답이 들어오는 읽기 기능을 수행하고 "end wait"메시지가 표시됩니다.

문제는지도에 둘 이상의 클라이언트가있는 경우입니다. "4 연결되지 연결된 클라이언트 스레드"

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

출력 읽 열리고 소켓에 연결 SendMessageToClient 함수의 부분에서는, 다음 식별 코드 실패.

내가이 오류 코드를 위로 보았다, 그것은 이렇게 설명 :

#define EINTR   4  /* Interrupted system call */ 

나는, 인터넷에 주변 검색 내가 찾은 모든 신호에 의해 중단되고 호출 시스템에 대한 몇 가지 언급했다.

메시지 보내기 기능을 한 번에 하나씩 호출 할 때이 기능이 작동하는 이유를 아는 사람이 있습니까?하지만 비동기를 사용하여 메시지 보내기 기능을 호출하면 실패합니다. 누구든지 여러 클라이언트에게 메시지를 보내야하는 다른 제안이 있습니까?

답변

0

먼저 EINTR 문제를 해결하려고합니다. connect()가 중단되었으므로 (이것은 EINTR의 의미입니다) 사용중인 설명자와 비동기 설명자 때문에 다시 시도하지 않습니다. 내가 그런 상황에서 일반적으로하는 일은 다시 시도하는 것입니다. 나는 잠시 동안 함수 (이 경우 연결)를 래핑합니다. 연결이 성공하면 나는 사이클을 벗어난다. 실패하면 errno의 값을 검사합니다. EINTR이면 다시 시도합니다. 재시도가 필요한 다른 값이 있음을 염두에 두십시오 (EWOULDBLOCK 중 하나임)