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)
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); }