2016-09-04 4 views
0

나는 winsock 및 C++를 사용하여 채팅 프로그램을 작성하는 것에 대한 자습서를 YouTube에서 보았습니다. 불행하게도이 튜토리얼은 경주 조건을 고려하는 것을 결코 괴롭히지 않았습니다.데이터를 클라이언트로 보내는 서버의 여러 스레드로부터 경쟁 조건을 피하는 방법은 무엇입니까? C++

이 튜토리얼에서는 새 클라이언트가 채팅 서버에 연결될 때마다 새 스레드를 열어 개별 클라이언트의 데이터 수신 및 처리를 처리하도록했습니다.

void Server::ClientHandlerThread(int ID) //ID = the index in the SOCKET Connections array 
{ 
Packet PacketType; 
while (true) 
{ 
    if (!serverptr->GetPacketType(ID, PacketType)) //Get packet type 
     break; //If there is an issue getting the packet type, exit this loop 
    if (!serverptr->ProcessPacket(ID, PacketType)) //Process packet (packet type) 
     break; //If there is an issue processing the packet, exit this loop 
} 
std::cout << "Lost connection to client ID: " << ID << std::endl; 
} 

클라이언트가 메시지를 전송하는 경우, 스레드를 처리 및 우선 메시지/패킷의 크기를 전송하는 패킷 타입을 전송하고, 최종적으로 메시지를 전송함으로써 전송할 것이다.

bool Server::SendString(int ID, std::string & _string) 
{ 
if (!SendPacketType(ID, P_ChatMessage)) 
    return false; 
int bufferlength = _string.size(); 
if (!SendInt(ID, bufferlength)) 
    return false; 
int RetnCheck = send(Connections[ID], _string.c_str(), bufferlength, NULL); //Send string buffer 
if (RetnCheck == SOCKET_ERROR) 
    return false; 
return true; 
} 

두 스레드 (두 개의 개별 클라이언트)가 동시에 동일한 ID로 메시지를 동 기적으로 보내려고 할 때 문제가 발생합니다. (동일한 세 번째 클라이언트). 하나의 스레드는 int 패킷 유형을 클라이언트에 보낼 수 있으므로 클라이언트는 이제 int를받을 준비가되었지만 두 번째 스레드는 문자열을 보냅니다. (스레드가 클라이언트가 기다리고 있다고 가정하기 때문에). 클라이언트가 올바르게 처리 할 수 ​​없으므로 프로그램을 사용할 수 없게됩니다.

어떻게이 문제를 해결할 수 있습니까?

내가 가진 한 가지 해결책은 다음과 같습니다. 각 스레드가 자체적으로 서버 명령을 실행할 수 있도록 허용하지 않고 입력 값을 설정합니다. 주 서버 쓰레드는 각 쓰레드의 모든 입력 값을 순환하여 명령을 하나씩 실행합니다.

그러나 나는 자신의 문제가 없을 것이라고 확신합니다 ... 클라이언트가 단일 서버 루프의 시간 프레임에서 여러 메시지를 보내는 경우 새 메시지가 끝날 것이므로 하나의 메시지 만 보내 게됩니다 이전 메시지를 쓰십시오). 물론 입력 또는 빠른 루프의 배열과 같은 방법이 있지만 여전히 문제가 있습니다.

내가 생각한 또 다른 문제는 더 낮은 ID를 가진 클라이언트가 항상 각 루프에 첫 번째로 보낸 메시지를 갖게된다는 것입니다. 이것은 큰 거래는 아니지만, 두 고객이 동일한 루프에서 정답을 입력 한 퀴즈 게임 인 경우, ID가 낮은 고객이 "첫 번째"라는 대답을 끝낼 것입니다 시각.

미리 감사드립니다.

+1

뮤텍스는 어떨까요? – Serge

+0

* 동일한 * 클라이언트가 동일한 소켓에서 여러 개의 독립적 인 요청을 동 기적으로 보냈고 각 요청을 관리하기 위해 여러 스레드를 시작했다고 말하는가? 그것은 바퀴가 왜건에서 떨어지고있는 곳인 것처럼 보일 것입니다. 각각의 * 요청 *을 각 * 연결이 아닌 별도의 스레드로 관리하려고하면 문제가되는 것 같습니다. 각 연결에는 인바운드 및 아웃 바운드 메시지의 자체 대기열 *이 있어야합니다. 만약 당신이 꼭해야만한다면, 당신은 각 발신 패킷에 대한 일련 번호를 정할 수 있지만 핵무기로 개미 언덕을 근절하는 것과 같은 정직한 채팅 프로그램에 대해서는 정직하게 말할 수 있습니다. – WhozCraig

+0

안녕하세요! 나는 정확하게 설명하지 않았을 것입니다. 각 클라이언트에는 단일 스레드/소켓 만 있습니다. 나는 내가 놓친 부분을보기 위해 내 글을 다시 읽을 것이다. 문제는 요청을 보내는 클라이언트와 같지 않지만 두 클라이언트가 동일한 세 번째 클라이언트에 요청을 동시에 전송합니다. "각 스레드마다 고유 한 대기열이 있어야합니다 ..."이것은 믿을지라도 여전히 동일한 문제가 발생합니다. 쓰레드가 모두 큐를 공유해야한다는 것을 의미하지 않는다면? 스레드가 여전히 자신의 대기열과 동 기적으로 작동하기 때문입니다. – Clint

답변

1

모든 I/O가 중앙 서버를 통해 처리되는 경우 간단한 (그러나 확실히 우아하지 않은) 해결책은 각 클라이언트에 대한 I/O 메커니즘을 둘러싼 장벽을 만드는 것입니다. 가장 간단한 경우 이것은 뮤텍스 일 수 있습니다. 해당 장벽을 각 클라이언트와 연결하고 누군가 해당 클라이언트에게 뭔가를 보내려고 할 때 (완료 메시지) 장벽을 잠급니다. 전체 메시지가 처리되면 잠금을 해제하십시오. 그렇게하면 한 번에 하나의 클라이언트 만 실제로 다른 클라이언트에 무언가를 보낼 수 있습니다. C++ 11에서는 std::mutex을 참조하십시오.

+0

이것은 ** 내가 ** 개인적으로 클라이언트를 디자인하는 방법이 아닌 ** <-server-> 클라이언트 유형의 프로토콜이지만 프로토콜과 응용 프로그램 디자인을 다르게 다룰 필요가있는 것처럼 들립니다. –