如何避免服务器的多个线程向客户端发送数据的竞态条件?c++

How would one avoid race conditions from multiple threads of a server sending data to a client? C++

本文关键字:数据 条件 c++ 客户端 服务器 何避免 线程      更新时间:2023-10-16

我正在跟随youtube上一个使用winsock和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的客户每次都会说出"第一个"答案。

提前感谢。

如果所有I/O都是通过中央服务器处理的,那么一个简单(但肯定不是很优雅)的解决方案是在每个客户机的I/O机制周围创建一个屏障。在最简单的情况下,这可能只是一个互斥对象。将该屏障与每个客户端关联,并且每当有人想要向该客户端发送某些内容(完整的消息)时,锁定该屏障。在处理完整消息时解锁它。这样,一次只有一个客户端可以实际地向另一个客户端发送一些内容。在c++ 11中,参见std::mutex