Асинхронный файловый ввод/вывод в операционной системе WINDOWS, страница 11

Листинг 2. Программа Talk

#include <windows.h>

#include <iostream.h>

#include <string.h>

#include <stdio.h> // sprintf

BOOL server=FALSE;

HANDLE OpenTalkPipe(char *host)

{

HANDLE rv;

if (!server)

{

char fn[1024];

sprintf(fn,"\\\\%s\\pipe\\talkpipe_awc",host);

cerr<<"Открываю канал: "<<fn<<"\n";

rv=CreateFile(fn, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

}

else

{

// программа в режиме сервера

rv=CreateNamedPipe("\\\\.\\pipe\\talkpipe_awc",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,1,1024,1024,1000,NULL);

}

return rv;

}

void main(int argc, char *argv[])

{

if (argc==1) server=TRUE;

HANDLE pipe=OpenTalkPipe(server?".":argv[1]);

HANDLE console=GetStdHandle(STD_INPUT_HANDLE);

HANDLE pipeevent=CreateEvent(NULL, TRUE, FALSE, NULL);

OVERLAPPED over;

char pipebuf;

DWORD inlen;

DWORD err;

if (pipe==INVALID_HANDLE_VALUE)

{

err=GetLastError();

cout<<"Ошибка при открытии канала ("<<err<<")\n";

exit(9);

}

over.Internal=over.InternalHigh=over.Offset=over.OffsetHigh=0;

over.hEvent=pipeevent;

if (!SetConsoleMode(console,0))

{

cerr<<"Error: не могу войти в режим консоли\n"<<GetLastError();

exit(4);

}

cout<<"Ожидание соединения\n";

cout.flush();

//Чтение канала

do {

while (ReadFile(pipe,&pipebuf,1,&inlen,&over)) cout<<pipebuf;

err=GetLastError();

} while (err==ERROR_PIPE_LISTENING);  // ожидание для клиента

cerr<<"Соединение установлено(^C для разрыва связи)\n";

if (err!=ERROR_IO_PENDING)

{

cerr<<"Ошибка чтения канала ("<<err<<")\n";

exit(3);

}

// Вход в режим ожидания

HANDLE waits[2];

DWORD obj;

waits[0]=pipeevent;

waits[1]=console;

while (1)

{

obj=WaitForMultipleObjects(2,(void **)&waits,FALSE,INFINITE);

// если ожидание прервано консолью, принимаем данные и шлем их в канал

if (obj==WAIT_OBJECT_0+1)

{

char inbuf; // только ANSI коды

OVERLAPPED dummy;

INPUT_RECORD irec;

dummy.hEvent=NULL;

dummy.Offset=dummy.OffsetHigh=dummy.Internal=dummy.InternalHigh=0;

DWORD inlen;

// Использование ReadConsole не сбрасывает сигнального состояния консоли

ReadConsoleInput(console,&irec,1,&inlen);

if (irec.EventType==KEY_EVENT&&irec.Event.KeyEvent.bKeyDown&&

irec.Event.KeyEvent.uChar.AsciiChar!='\0')

{

inbuf=irec.Event.KeyEvent.uChar.AsciiChar;

if (inbuf=='\003')

{

// Завершение

CloseHandle(pipe);

CloseHandle(pipeevent);

exit(0);

}

WriteFile(pipe,&inbuf,1,&inlen,&dummy);

// Эхо

cout<<((inbuf=='\r')?'\n':inbuf);

cout.flush();

// Ожидаем завершение записи

if (!GetOverlappedResult(pipe,&dummy,&inlen,TRUE))

cout<<"Неизвестная ошибка записи в канал!\n";

}

}

// если ожидание завершено, вывести символ на консоль

if (obj==WAIT_OBJECT_0)

{

DWORD len;

// Получить результат, несмотря на то, что мы о нем уже знаем

if (!GetOverlappedResult(pipe,&over,&len,TRUE))

{

cout<<"!Неизвестная ошибка консоли!\n";

exit(3);

}

do

{

if (pipebuf=='\r') cout<<'\n'; else cout<<pipebuf;

} while (ReadFile(pipe,&pipebuf,1,&inlen,&over));

DWORD err=GetLastError();

if (err!=ERROR_IO_PENDING)

{

cerr<<"Ошибка чтения канала\n";

exit(3);

}

cout.flush();

}

}

}

Клиент и сервер открывают канал для перекрывающегося ввода/вывода. Ввод с консоли осуществляется традиционным методом, так как при появлении на консоли данных для ввода дескриптор консоли автоматически переходит в сигнальное состояние, и программа может среагировать на это, даже если она не использует для ввода с консоли перекрывающегося ввода/вывода.