0

我用 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);
}
4

0 回答 0