Tag: SO2 – 1920 – Ficha 7 v.2.3 – Named Pipes

Sessão 2/2 – Named Pipes, overlapped IO, duplex

Comunicação assíncrona – overlapped I/O
operações que normalmente bloqueiam até completarem (read, write) podem ser completadas em background pelo SO
isto consegue-se passando um ponteiro não nulo para uma estrutura do tipo OVERLAPPED
se na estrutura for passado um handle para um evento, esse evento é assinalado quando a operação for completada pelo SO

//escritor.c 2ª versao
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define PIPE_NAME TEXT("\\\\.\\pipe\\teste")
#define TAM 256
#define PIPE_NMAX_INSTANCIAS 10

typedef struct {
    HANDLE hInstance;
    OVERLAPPED overlap;
    BOOL active; //se ja tem um cliente activo ou nao
} PIPEDATA;


PIPEDATA hPipes[PIPE_NMAX_INSTANCIAS];
HANDLE hEvents[PIPE_NMAX_INSTANCIAS], hMutex;
int termina = 0;


DWORD WINAPI ThreadMensagens(LPVOID param)
{
    DWORD n;
    int i;
    TCHAR buf[TAM];
    	
    do {
        _tprintf(TEXT("[ESCRITOR] Frase: "));
        _fgetts(buf, 256, stdin);
        buf[_tcslen(buf) - 1] = '\0';

        WaitForSingleObject(hMutex, INFINITE);
    	
        for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) {
            if (hPipes[i].active) {
                    if (!WriteFile(hPipes[i].hInstance, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) {
                        _tprintf(TEXT("[ERRO] Escrever no pipe %d! (WriteFile)\n"),i);
                        exit(-1);
                    }else{
                        _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i);
                    }
                }
            }
        ReleaseMutex(hMutex);
    }
	while (_tcscmp(buf, TEXT("fim")));
	
	//terminar
    termina = 1;

    for (i = 0; i < PIPE_NMAX_INSTANCIAS; i++)
    {
        SetEvent(hEvents[i]);
    }
    
	return 0;
}

int _tmain(int argc, LPTSTR argv[]) {
    int i;
    int numClientes = 0;
    HANDLE hEventTemp, hPipeTemp, hThread;
    DWORD waitOffset, nbytes;

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

	hMutex = CreateMutex(NULL, FALSE, NULL);
	if(hMutex == NULL)
	{
        _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n"));
        exit(-1);
	}
	
    for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) {
        _tprintf(TEXT("[ESCRITOR] Criar uma cópia %d do pipe '%s' ... (CreateNamedPipe)\n"), i, PIPE_NAME);

        hPipeTemp = CreateNamedPipe(
            PIPE_NAME,
            PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
            PIPE_WAIT |
            PIPE_TYPE_MESSAGE |
            PIPE_READMODE_MESSAGE,
            PIPE_NMAX_INSTANCIAS, //mais clientes
            256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256];
            256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256];
            1000,
            NULL //
        );
        if (hPipeTemp == INVALID_HANDLE_VALUE) {
            _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)"));
            exit(-1);
        }

        _tprintf(TEXT("[ESCRITOR] Esperar ligação overlapped do leitor %d... (ConnectNamedPipe)\n"), i);

        hPipes[i].hInstance = hPipeTemp;
        hEventTemp = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (hEventTemp == NULL) {
            _tprintf(TEXT("[ERRO] Criar Event! (CreateEvent)"));
            exit(-1);
        }
        ZeroMemory(&hPipes[i].overlap, sizeof(hPipes[i].overlap));
        hEvents[i] = hEventTemp;
        hPipes[i].overlap.hEvent = hEvents[i];


        if (ConnectNamedPipe(hPipeTemp, &hPipes[i].overlap) != 0) { //operação bloqueante até que aja um cliente
            _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n"));
            exit(-1);
        }
        hPipes[i].active = FALSE;
    }
    hThread = CreateThread(NULL, 0, ThreadMensagens, NULL, 0, NULL);

    if (hThread == NULL)
    {
        _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n"));
        exit(-1);
    }


    while(!termina && numClientes < PIPE_NMAX_INSTANCIAS)
    {
        _tprintf(TEXT("[ESCRITOR] Esperar pela ligçaão de leitor %d (WaitForMultipleObjects + GetOverlappedResult)\n"), numClientes);

        i = waitOffset - WAIT_OBJECT_0;
        _tprintf(TEXT("[ESCRITOR] Novo i = %d\n"), i);
        if(i>= 0 && i < PIPE_NMAX_INSTANCIAS)
        {
            ResetEvent(hEvents[i]);
            WaitForSingleObject(hMutex, INFINITE);
            _tprintf(TEXT("Ativar pipe %d\n"), i);
            hPipes[i].active = TRUE;
            ReleaseMutex(hMutex);
            numClientes++;
        }
    	
    }
    WaitForSingleObject(hThread, INFINITE);
    _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n"));

	for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) {
        
        if (!DisconnectNamedPipe(hPipes[i].hInstance)) {
            _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)"));
            exit(-1);
        }
        CloseHandle(hPipes[i].hInstance);
    }
    exit(0);
}

encontrei mais informações sobre este exemplo em:
https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-overlapped-i-o
e a Comunicação bidirecional (duplex)

Tags : , , , ,

Sessão 1/2 – Named Pipes

Interprocessos (processo a falar uns com os outros):
Memoria partilhada
Named pipes

Threads do mesmo processo:
espaço de endereçamento do processo

Named pipes:
paradigma cliente <–> servidor
Um pipe com múltiplas instâncias, para resolver o problema de cinco clientes e um servidor
Nos namedPipes permitem comunicação bidirecional (se tiverem permissões de escrita e leitura para o cliente e servidor, mas não pode ser em simultâneo)

Servidor:
Tem uma thread para cada cliente
1º CreateNamedPipe
2º ConnectNamedPipe
3º Thread Secundária para atender os clientes
4º (fechar) FlushFileBuffers
5º (fechar) DisconnecNamedPipes
6º (fechar) CloseHandle

Cliente:
1º (ligar) CreateFile ou CallNamedPipe
2º (aguarda pela instância) WaitNamedPipe
3º SetNamedPipeHandleState
4º fechar os handles do processo

exemplos base

//escritor.c
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define PIPE_NAME TEXT("\\\\.\\pipe\\teste")

int _tmain(int argc, LPTSTR argv[]) {
    DWORD n;
    HANDLE hPipe;
    TCHAR buf[256];

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME);
    hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_WAIT |
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1,
        sizeof(buf), sizeof(buf), 1000, NULL);
    if (hPipe == INVALID_HANDLE_VALUE) {
        _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)"));
        exit(-1);
    }

    while (1) {

        _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n"));
        if (!ConnectNamedPipe(hPipe, NULL)) {
            _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n"));
            exit(-1);
        }

        do {
            _tprintf(TEXT("[ESCRITOR] Frase: "));
            _fgetts(buf, 256, stdin);
            buf[_tcslen(buf) - 1] = '\0';
            if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) {
                _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n"));
                exit(-1);
            }
            _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n);
        } while (_tcscmp(buf, TEXT("fim")));

        _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n"));
        if (!DisconnectNamedPipe(hPipe)) {
            _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)"));
            exit(-1);
        }
    }
    Sleep(2000);
    CloseHandle(hPipe);
    exit(0);
}
//leitor.c
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

// ...
#define PIPE_NAME TEXT("\\\\.\\pipe\\teste")

int _tmain(int argc, LPTSTR argv[]) {
    TCHAR buf[256];
    HANDLE hPipe;
    int i = 0;
    BOOL ret;
    DWORD n;

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME);
    if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) {
        _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME);
        exit(-1);
    }
    _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n"));
    hPipe = CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);
    if (hPipe == NULL) {
        _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME);
        exit(-1);
    }
    _tprintf(TEXT("[LEITOR] Liguei-me...\n"));

    while (1) {
        ret = ReadFile(hPipe, buf, sizeof(buf), &n, NULL);
        buf[n / sizeof(TCHAR)] = '\0';
        if (!ret || !n) {
            _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n);
            break;
        }
        _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf);
    }
    CloseHandle(hPipe);
    Sleep(200);
    return 0;
}

Nova versão com comnetários:

//escritor.c
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define PIPE_NAME TEXT("\\\\.\\pipe\\teste") 

int _tmain(int argc, LPTSTR argv[]) {
    DWORD n;
    HANDLE hPipe;
    TCHAR buf[256];

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME);
    hPipe = CreateNamedPipe(
        PIPE_NAME, //nome pipe
        PIPE_ACCESS_OUTBOUND, //modo de abertura do pipe só o servidor escreve
        PIPE_WAIT | // bloqueante: bloqueio ate escrever
        PIPE_TYPE_MESSAGE | // organização da informação: agrupa bytes em mensagens na escrita
        PIPE_READMODE_MESSAGE, //  organização da informação: agrupa bytes em mensagens na leitura
        1, // numero maximo de instancias
        sizeof(buf), // tamanho da mensagem
        sizeof(buf), // tamanho da mensagem
        1000, // tempo de espera do cliente para usar no: waitNamedPipe (cliente)
        NULL //
    );
    if (hPipe == INVALID_HANDLE_VALUE) {
        _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)"));
        exit(-1);
    }

    while (1) {

        _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n"));
    	
        if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente
            _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n"));
            exit(-1);
        }

        do {
            _tprintf(TEXT("[ESCRITOR] Frase: "));
            _fgetts(buf, 256, stdin);
            buf[_tcslen(buf) - 1] = '\0';
            if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) {
                _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n"));
                exit(-1);
            }
            _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n);
        } while (_tcscmp(buf, TEXT("fim")));

        _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n"));
    	
        if (!DisconnectNamedPipe(hPipe)) {
            _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)"));
            exit(-1);
        }
    }
    Sleep(2000);
    CloseHandle(hPipe);
    exit(0);
}
//leitor.c ou cliente.c
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define PIPE_NAME TEXT("\\\\.\\pipe\\teste")

int _tmain(int argc, LPTSTR argv[]) {
    TCHAR buf[256];
    HANDLE hPipe;
    int i = 0;
    BOOL ret;
    DWORD n;

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME);
	
    if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) { //garantir que existe do lado do servidor algo ou definir um tempo ms
        _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME);
        exit(-1);
    }
    _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n"));
	
    hPipe = CreateFile(
        PIPE_NAME, // no do ficheiro, nome do namedPipe
        GENERIC_READ, // (1) 
        0, // nao se aplica
        NULL, // nao interessa
        OPEN_EXISTING, // sempre OPEN, pois já esta criada
        FILE_ATTRIBUTE_NORMAL, // por default
        NULL // nao interessa
    );
	//(1)
    //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_OUTBOUND então aqui tem que ser GENERIC_READ
    //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_INBOUND então aqui tem que ser GENERIC_WRITE
    //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_DUPLEX então aqui tem que ser GENERIC_READ | GENERIC_WRITE
	
    if (hPipe == NULL) {
        _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME);
        exit(-1);
    }
    _tprintf(TEXT("[LEITOR] Liguei-me...\n"));

    while (1) {
        ret = ReadFile(
            hPipe, // handle para o pipe
            buf, // onde vai colocar a informação lida
            sizeof(buf), // a quantidade máxima que pode ler
            &n, // escreve o numero de bytes que foi lida, inclui o \0
            NULL // overllaped nao estamos a usar
        );
        buf[n / sizeof(TCHAR)] = '\0'; //ler o \0 no fim da string
        if (!ret || !n) {
        	//quando surge o disconnect por parte do servidor
            _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n);
            break;
        }
        _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf);
    }
    CloseHandle(hPipe);
    Sleep(200);
    return 0;
}

uma versão nova do escritor/servidor para aceitar vários clientes:

//escritor.c 2ª versao
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define PIPE_NAME TEXT("\\\\.\\pipe\\teste")

#define PIPE_NMAX_INSTANCIAS 10

typedef struct{
    HANDLE hPipesCliente[PIPE_NMAX_INSTANCIAS];
    int numeroClientes;
    HANDLE hMutex;	//mutexs acesso 
    int terminar; //controlar o fim da thread
} ThreadDados;

//a thread

DWORD WINAPI ThreadMensagens(LPVOID param)
{
    ThreadDados* dados = (ThreadDados*)param;
    TCHAR buf[256];
    DWORD n;
	
    do {
        _tprintf(TEXT("[ESCRITOR] Frase: "));
        _fgetts(buf, 256, stdin);
        buf[_tcslen(buf) - 1] = '\0';

        WaitForSingleObject(dados->hMutex, INFINITE);
    	//região critica.. dados->numeroClientes, dados->hPipesCliente[i]
    	for(int i = 0; i < dados->numeroClientes; i++){
	        if (!WriteFile(dados->hPipesCliente[i], buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) {
	            _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n"));
	            exit(-1);
	        }
	        _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i);
        }
    	//fim da região cirtica
        ReleaseMutex(dados->hMutex);
    }
	while (_tcscmp(buf, TEXT("fim")));
	
	//terminar
    dados->terminar = 1;

    //i) simular o criar um novo cliente, para o "fim" funcionar
    CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	return 0;
}

int _tmain(int argc, LPTSTR argv[]) {
    HANDLE hPipe;

	//thread
    ThreadDados dados;
	//f)
    HANDLE hThread;

#ifdef UNICODE
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

    dados.numeroClientes = 0;
    dados.terminar = 0;
	//criar mutex
    dados.hMutex = CreateMutex(NULL, FALSE, NULL);
	if(dados.hMutex == NULL)
	{
        _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n"));
        exit(-1);
	}
	//g)
    hThread = CreateThread(NULL, 0, ThreadMensagens, &dados, 0, NULL);

	if(hThread == NULL)
	{
        _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n"));
        exit(-1);
	}

    
	//e)
    while (!dados.terminar) {
    	
        _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME);
    	
        hPipe = CreateNamedPipe(
            PIPE_NAME,
            PIPE_ACCESS_OUTBOUND,
            PIPE_WAIT |
            PIPE_TYPE_MESSAGE |
            PIPE_READMODE_MESSAGE,
            PIPE_NMAX_INSTANCIAS, //mais clientes
            256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256];
            256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256];
            1000,
            NULL //
        );
        if (hPipe == INVALID_HANDLE_VALUE) {
            _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)"));
            exit(-1);
        }
    	
        _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n"));
    	
        if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente
            _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n"));
            exit(-1);
        }
        //b)
        WaitForSingleObject(dados.hMutex, INFINITE);
        //a) ciclo de aceitação dos novos clientes
        dados.hPipesCliente[dados.numeroClientes] = hPipe;
        dados.numeroClientes++;
    	//b)
        ReleaseMutex(dados.hMutex);
    }

    //h) esperar que a thread termine
    WaitForSingleObject(hThread, INFINITE);
	
    //c) encerrar com todos os clientes
    for (int i = 0; i < dados.numeroClientes; i++) {
        _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n"));
        if (!DisconnectNamedPipe(dados.hPipesCliente[i])) {
            _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)"));
            exit(-1);
        }
    }
    exit(0);
}
Tags : , , , ,