Servidor de Jogo com Named Pipes e Threads em C
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;
}