我用 C++ 编写了一个套接字类,该设计是基于事件的,这意味着每 64 个玩家将创建一个新线程来处理这些套接字。我的问题是,当我开始读取文件、写入,甚至复制和粘贴时,它显示“Server.exe 没有响应”,当进程完成(磁盘)时,它又开始响应,断开我的一些播放器服务器。
这是我的听众:
void ServerSocket::ListenerThread(ServerSocket * Server)
{
// Inicializa a lista de espera.
InitializeCriticalSection(&Server->m_EventCheckers.Lock);
InitializeCriticalSection(&Server->m_Pending.Lock);
bool TerminateThread = false;
DWORD Result = 0;
WSANETWORKEVENTS NetEvents = { 0 };
// Enquanto for permitida a leitura de novas conexões.
while (WaitForSingleObject(Server->m_Listener.WaitObject, FALSE) != WAIT_OBJECT_0)
{
Result = WSAWaitForMultipleEvents(1, &Server->m_Listener.Event, FALSE, 2500, FALSE);
if (Result == WSA_WAIT_EVENT_0)
{
// Existe algo pendente no soquete.
// Enumera os eventos pendentes.
Result = WSAEnumNetworkEvents(Server->m_Listener.Socket, Server->m_Listener.Event, &NetEvents);
if (Result != SOCKET_ERROR)
{
// Verifica qual evento está ativo.
if (NetEvents.lNetworkEvents & FD_ACCEPT)
{
INT ErrorCode = NetEvents.iErrorCode[FD_ACCEPT_BIT];
if (ErrorCode == NULL)
{
// Aceita uma nova conexão.
Client* User = new Client();
if (User->AcceptFromSocket(Server->m_Listener.Socket))
{
int LastUserCount;
// Coloca na lista de espera e verifica as procedures.
EnterCriticalSection(&Server->m_Pending.Lock);
LastUserCount = Server->m_ConnectionCount++;
Server->m_Pending.Clients.push_front(User);
LastUserCount += Server->m_Pending.Clients.size();
LeaveCriticalSection(&Server->m_Pending.Lock);
EnterCriticalSection(&Server->m_EventCheckers.Lock);
if (Server->m_EventCheckers.Runnings.size() * WSA_MAXIMUM_WAIT_EVENTS < LastUserCount)
{
std::shared_ptr<ThreadInformation> Information = std::make_shared<ThreadInformation>();
Information->EventObject = CreateEventA(NULL, TRUE, FALSE, NULL);
Information->Thread.Initialize(std::thread(EventChecker, Server, Information));
Server->m_EventCheckers.Runnings.push_back(Information);
char szMessage[256] = { 0 };
sprintf_s(szMessage, "Número de Threads( %d ) -> Jogadores( %d )",
Server->m_EventCheckers.Runnings.size(), LastUserCount);
World::ServerManager->LogFile("Socket", szMessage);
}
LeaveCriticalSection(&Server->m_EventCheckers.Lock);
}
else
{
// Algum erro ocorreu durante o accept.
delete User;
}
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
else if (NetEvents.lNetworkEvents & FD_CLOSE)
{
INT ErrorCode = NetEvents.iErrorCode[FD_CLOSE_BIT];
if (ErrorCode == NULL)
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
}
else
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
else if (Result != WSA_WAIT_TIMEOUT)
{
// Fecha a escuta.
SetEvent(Server->m_Listener.WaitObject);
TerminateThread = true;
}
}
if (TerminateThread)
{
Server->m_Listener.WaitObject = INVALID_HANDLE_VALUE;
WSACloseEvent(Server->m_Listener.Event);
closesocket(Server->m_Listener.Socket);
Log("Function: ServerSocket::ListenerThread(). Details: TerminateThread is true.\n");
}
// Deleta a lista de espera.
DeleteCriticalSection(&Server->m_Pending.Lock);
DeleteCriticalSection(&Server->m_EventCheckers.Lock);
}
这是我处理套接字的地方。
void ServerSocket::EventChecker(ServerSocket * Server, std::shared_ptr<ThreadInformation> Information)
{
SOCKET Sockets[WSA_MAXIMUM_WAIT_EVENTS] = { INVALID_SOCKET };
WSAEVENT Events[WSA_MAXIMUM_WAIT_EVENTS] = { WSA_INVALID_EVENT };
DWORD LastConnection = GetCurrentTime();
DWORD LastUserCheck = NULL;
DWORD Result = NULL;
WSANETWORKEVENTS NetworkEvents = { 0 };
std::vector<std::shared_ptr<Client>> Clients;
// Predicate to Erase-Remove Idiom.
auto UserPredicate = [&](std::shared_ptr<Client> User)
{
if (!User || User->GetSocket() == INVALID_SOCKET || User->GetEvent() == WSA_INVALID_EVENT || User->GetFreeFlag())
return true;
return false;
};
while (WaitForSingleObject(Information->EventObject, FALSE) != WAIT_OBJECT_0)
{
// Se não tiver clientes conectados, sai da thread para economizar CPU.
if (Clients.empty() && GetCurrentTime() - LastConnection > 150)
{
SetEvent(Information->EventObject);
continue;
}
// Verifica se esta na hora de pegar os usuários na lista de espera.
if (LastUserCheck + 2000 < GetCurrentTime() && Clients.size() < WSA_MAXIMUM_WAIT_EVENTS)
{
// Possui algum usuário na lista de espera.
EnterCriticalSection(&Server->m_Pending.Lock);
// Obtêm o usuário da lista.
for (size_t i = 0; i < Server->m_Pending.Clients.size(); i++)
{
if (Clients.size() < WSA_MAXIMUM_WAIT_EVENTS)
{
Client * User = Server->m_Pending.Clients.back();
if (User)
{
// Obtêm o ownership do usuário com um ponteiro compartilhado.
Clients.push_back(std::move(std::shared_ptr<Client>(User)));
Server->m_Pending.Clients.pop_back();
}
}
else
break;
}
LeaveCriticalSection(&Server->m_Pending.Lock);
LastUserCheck = GetCurrentTime();
}
// Verifica por conexões não autenticadas.
for (int i = 0; i < (int)Clients.size(); i++)
{
if (!Clients[i]->isInitialized() && !Clients[i]->GetPlayerData())
{
if (Clients[i]->GetLastRecvTime() + Connection::MaxAutorizationTime < GetCurrentTime())
{
// Percorre todos os jogadores para desconectar o IP.
for (int c = 0; c < (int)Clients.size(); c++)
{
if (_strcmpi(Clients[i]->GetIP(), Clients[c]->GetIP()) == 0)
{
Clients[c]->CloseSocket();
}
}
}
}
}
// Verifica por conexões inválidas de time-out.
for (int i = 0; i < (int)Clients.size(); i++)
{
if (Clients[i]->GetLastRecvTime() + (60 * 1000) < GetCurrentTime())
{
if (!Clients[i]->isReceivingUpdate())
{
Clients[i]->CloseSocket();
}
}
}
// Verifica por eventos inválidos ou jogadores "recém desconectados".
Clients.erase(std::remove_if(Clients.begin(), Clients.end(), UserPredicate), Clients.end());
// Separa os eventos e os soquetes.
for (unsigned i = 0; i < Clients.size(); i++)
{
Sockets[i] = Clients[i]->GetSocket();
Events[i] = Clients[i]->GetEvent();
}
// Caso não exista um usuário, apenas continua o loop.
if (Clients.empty())
continue;
LastConnection = GetCurrentTime();
Result = WSAWaitForMultipleEvents(Clients.size(), Events, FALSE, 1000, FALSE);
if (Result != SOCKET_ERROR)
{
if (Result >= WSA_WAIT_EVENT_0 && Result < WSA_MAXIMUM_WAIT_EVENTS + WSA_WAIT_EVENT_0)
{
INT Index = Result - WSA_WAIT_EVENT_0;
if (Clients.size() >= 0 && Index < int(Clients.size()))
{
std::shared_ptr<Client> & User = Clients[Index];
Result = WSAEnumNetworkEvents(User->GetSocket(), User->GetEvent(), &NetworkEvents);
if (Result != SOCKET_ERROR)
{
INT ErrorCode = 0;
if (NetworkEvents.lNetworkEvents & FD_READ)
{
ErrorCode = NetworkEvents.iErrorCode[FD_READ_BIT];
if (ErrorCode == FALSE)
{
// O soquete está com dados pendentes.
char DataReceived[Connection::MAX_BufferSize] = { 0 };
int Len = recv(User->GetSocket(), DataReceived, sizeof(DataReceived), NULL);
if (Len > 0)
{
if (User->isFirstRecv())
{
if (Len >= 4)
{
User->Initialize();
}
else
{
Clients.erase(Clients.begin() + Index);
continue;
}
}
if (!User->PushRecv(DataReceived, Len))
{
Clients.erase(Clients.begin() + Index);
continue;
}
}
else
{
Clients.erase(Clients.begin() + Index);
}
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_ReadBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
else if (NetworkEvents.lNetworkEvents & FD_WRITE)
{
ErrorCode = NetworkEvents.iErrorCode[FD_WRITE_BIT];
if (ErrorCode == FALSE)
{
// O soquete está livre para mandar dados.
User->WriteData();
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_WriteBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
else if (NetworkEvents.lNetworkEvents & FD_CLOSE)
{
ErrorCode = NetworkEvents.iErrorCode[FD_CLOSE_BIT];
if (ErrorCode == FALSE)
{
User->GracefulShutdown();
Clients.erase(Clients.begin() + Index);
continue;
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: FD_CloseBit is %d.\n", ErrorCode);
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
}
else
{
Log("Function: ServerSocket::RunSocketProcedure(). Details: Network ErrorCode is %d.\n", WSAGetLastError());
Server->AddLostConnection(Clients[Index]);
Clients.erase(Clients.begin() + Index);
continue;
}
}
}
}
else if (Result != WSA_WAIT_TIMEOUT)
{
char szMessage[256] = { 0 };
sprintf_s(szMessage, "WSAWaitForMultipleEvents Error Code [ %d ]", Result);
World::ServerManager->LogFile("Socket", szMessage);
Sleep(100);
}
}
EnterCriticalSection(&Server->m_EventCheckers.Lock);
Information->EventObject = INVALID_HANDLE_VALUE;
for (unsigned i = 0; i < Server->m_EventCheckers.Runnings.size(); i++)
{
if (Server->m_EventCheckers.Runnings[i] == Information)
{
Server->m_EventCheckers.Runnings.erase(Server->m_EventCheckers.Runnings.begin() + i);
break;
}
}
LeaveCriticalSection(&Server->m_EventCheckers.Lock);
}