Servidor de Jogo com Named Pipes e Threads em C

Eletrônica5,03 KB

Servidor de Jogo com Named Pipes e Threads em C (Win32)

Implementação de um servidor multithread para jogos de perguntas e respostas em ambiente Windows, utilizando Named Pipes para comunicação entre processos (IPC) e Mutexes para sincronização de memória partilhada.

Constantes e Estruturas de Dados

#define MAX_CLIENTES 30
#define PIPE_NAME    TEXT("\\\\.\\pipe\\NPIPEJOGO")

typedef struct {
    HANDLE hPipe;
    TCHAR  nome[201];
    int    pontos;
} CLIENTE;

// Dados partilhados
static CLIENTE clientes[MAX_CLIENTES];
static int     nClientes = 0;
static TCHAR   perguntaAtual[201];
static HANDLE  hMutex;    // Protege o acesso a clientes[] e perguntaAtual

void obtemPergunta(TCHAR *bufferAPreencher);
BOOL validaResposta(TCHAR *pergunta, TCHAR *resposta);

Funções Auxiliares: Difusão e Remoção

static void BroadcastString(const TCHAR *msg) {
    // Envia uma string a todos os clientes ligados
    DWORD written;
    WaitForSingleObject(hMutex, INFINITE);
    for (int i = 0; i < nClientes; i++) {
        WriteFile(
            clientes[i].hPipe,
            msg,
            (lstrlen(msg) + 1) * sizeof(TCHAR),
            &written,
            NULL
        );
    }
    ReleaseMutex(hMutex);
}

static void RemoveCliente(HANDLE hPipe) {
    // Remove um cliente da lista (chamar dentro de hMutex)
    for (int i = 0; i < nClientes; i++) {
        if (clientes[i].hPipe == hPipe) {
            // Compacta o array
            for (int j = i; j + 1 < nClientes; j++)
                clientes[j] = clientes[j + 1];
            nClientes--;
            break;
        }
    }
}

Thread de Atendimento ao Cliente

DWORD WINAPI ClienteThread(LPVOID pv) {
    // Thread que trata um cliente
    CLIENTE *c = (CLIENTE*)pv;
    TCHAR    buf[201];
    DWORD    lidos;

    // 1) Lê o nome do utilizador
    if (!ReadFile(c->hPipe, buf, sizeof(buf), &lidos, NULL) || lidos == 0)
        goto cleanup;
    buf[lidos / sizeof(TCHAR)] = 0;
    _tcsncpy_s(c->nome, buf, _TRUNCATE);
    c->pontos = 0;

    // 2) Logo que entra, envia a pergunta atual
    WaitForSingleObject(hMutex, INFINITE);
    WriteFile(c->hPipe,
              perguntaAtual,
              (lstrlen(perguntaAtual) + 1) * sizeof(TCHAR),
              &lidos,
              NULL);
    ReleaseMutex(hMutex);

    // 3) Loop de respostas
    while (TRUE) {
        if (!ReadFile(c->hPipe, buf, sizeof(buf), &lidos, NULL) || lidos == 0)
            break;
        buf[lidos / sizeof(TCHAR)] = 0;
        if (_tcscmp(buf, TEXT("#adeus")) == 0)
            break;

        // Testa a resposta
        WaitForSingleObject(hMutex, INFINITE);
        if (validaResposta(perguntaAtual, buf)) {
            c->pontos++;
            // Notifica a todos
            TCHAR msg[256];
            _stprintf_s(msg, 256,
                        TEXT("%s acertou! Pontos: %d\n"),
                        c->nome, c->pontos);
            BroadcastString(msg);
            // Nova pergunta
            obtemPergunta(perguntaAtual);
            BroadcastString(perguntaAtual);
        }
        // Se falhar, ignora
        ReleaseMutex(hMutex);
    }

cleanup:
    // Remoção e cleanup
    WaitForSingleObject(hMutex, INFINITE);
    RemoveCliente(c->hPipe);
    ReleaseMutex(hMutex);

    DisconnectNamedPipe(c->hPipe);
    CloseHandle(c->hPipe);
    return 0;
}

Ciclo Principal do Servidor

int _tmain(int argc, TCHAR *argv[]) {
    // 1) Cria mutex
    hMutex = CreateMutex(NULL, FALSE, NULL);

    // 2) Obtém a primeira pergunta
    obtemPergunta(perguntaAtual);

    // 3) Loop de aceitação de clientes
    for (;;) {
        // Cria instância do pipe
        HANDLE hPipe = CreateNamedPipe(
            PIPE_NAME,
            PIPE_ACCESS_DUPLEX,
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
            MAX_CLIENTES,
            4096, 4096, 0, NULL
        );

        if (hPipe == INVALID_HANDLE_VALUE) {
            _tprintf(TEXT("Falha CreateNamedPipe: %d\n"), GetLastError());
            return 1;
        }

        // Espera ligação
        BOOL ok = ConnectNamedPipe(hPipe, NULL) ||
                  GetLastError() == ERROR_PIPE_CONNECTED;
        if (!ok) {
            CloseHandle(hPipe);
            continue;
        }

        // Adiciona cliente ou rejeita se cheio
        WaitForSingleObject(hMutex, INFINITE);
        if (nClientes < MAX_CLIENTES) {
            clientes[nClientes].hPipe = hPipe;
            CreateThread(NULL, 0, ClienteThread, &clientes[nClientes], 0, NULL);
            nClientes++;
        } else {
            DWORD w;
            WriteFile(hPipe,
                      TEXT("Sala cheia. Tente mais tarde.\n"),
                      24 * sizeof(TCHAR),
                      &w, NULL);
            DisconnectNamedPipe(hPipe);
            CloseHandle(hPipe);
        }
        ReleaseMutex(hMutex);
    }

    // (Nunca chega aqui)
    CloseHandle(hMutex);
    return 0;
}

Baixar

PDF Word HTML