Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Работа с Named Pipe


Автор: Usper 19.10.2009, 08:45
Для разрабатываемого приложения требуется межпроцессное взаимодействие. Выбор пал на именованые каналы. Всё вроде бы работает, но только до тех пор, пока клиент не закроет канал со своей стороны. После этого клиент повторно присоединиться к серверу не может, функция CreateFile() возвращает INVALID_HANDLE_VALUE. Что должен делать сервер, после того как канал будет закрыт со стороны клиента? Создать канал заново? Как серверу определить, что клиент уже закрыл канал?

Код


HANDLE hPipe;

void __fastcall TMonPipe::Execute()
{
    char pBuf[BufSize];
    unsigned long cbRead;
    hPipe = CreateNamedPipe("\\\\.\\pipe\\$MonitorPipe$",PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES, BufSize, BufSize, 1000, NULL);
    if(hPipe==INVALID_HANDLE_VALUE)
    {
        ErrorMessage="Не удалось открыть канал взаимодействия приложений.";
        Synchronize(CallShowMessageFunction);
        return;
    }
    while(!Terminated)
    {
        UpdCapt="Нет коннекта";
        Synchronize(UpdateCaption);
        if(ConnectNamedPipe(hPipe, NULL))
        {
            UpdCapt="Есть коннект";
            Synchronize(UpdateCaption);
            while(!Terminated)
            {
                if(ReadFile(hPipe, pBuf, BufSize, &cbRead, NULL))
                {
                    if(AnsiString(pBuf)=="Test") {SendMessage("Test - Ok"); Synchronize(UpdateCaption);}
                    Synchronize(UpdateCaption);

                }
                else
                    /*if(GetLastError()==ERROR_PIPE_CONNECTED)*/ break;
            }
         }
    }
}

Автор: xvr 19.10.2009, 14:10
Сервер должен создать (позвать CreateNamedPipe) столько именнованных каналов, сколько он хочет обслужить клиентов зараз (т.е. в вашем случае 1000 раз)
Каждый вызов ConnectNamedPipe подключает ОДНОГО клиента, для подключения следующего клиента сервер опять должен позвать ConnectNamedPipe

Цитата

Как серверу определить, что клиент уже закрыл канал?
Сервер получит ошибку при попытке читать или писать в такой канал.
IMHO есть и явные индикаторы закрытия канала, см MSDN

Автор: Usper 19.10.2009, 15:39
Да нет, мне не нужно одновременно работать с тысячью клиентов (с чего вы взяли?). Достаточно и одного. Но в любом случае, что тогда означет параметр nMaxInstances в функции CreateNamedPipe?
Цитата

nMaxInstances

Specifies the maximum number of instances that can be created for this pipe. The same number must be specified for all instances. Acceptable values are in the range 1 through PIPE_UNLIMITED_INSTANCES. If this parameter is PIPE_UNLIMITED_INSTANCES, the number of pipe instances that can be created is limited only by the availability of system resources. 


Автор: xvr 19.10.2009, 15:56
Цитата(Usper @ 19.10.2009,  15:39)
Да нет, мне не нужно одновременно работать с тысячью клиентов (с чего вы взяли?). Достаточно и одного. Но в любом случае, что тогда означет параметр nMaxInstances в функции CreateNamedPipe?

Вот это собственно и значит - количество инстансов именновоного пайпа, которые вы собираетесь создать. (Отсюда я и взял число 1000 Пардон, не туда посмотрел - у вас вообще PIPE_UNLIMITED_INSTANCES)
Но этот параметр никоим образом не заставляет Винду создать все эти инстантсы  smile Результат ОДНОГО вызова CreateNamedPipe может одновременно обработать не более ОДНОГО клиента. Если вам нужно больше - создавайте еще (сами)

После отключения клиента, сервер должен позвать DisconnectNamedPipe на хэндлере, где был клиент, а потом может снова звать ConnectNamedPipe

Автор: Usper 20.10.2009, 09:19
Спасибо за советы, всё заработало. Однако аппетит приходит во время еды  smile. Сейчас столкнулся с тем, что клиент во время чтения из канала виснет намертво, если сервер ничего не пишет в канал. То есть функция ReadFile() бесконечно ждёт информацию от сервера. Как этого избежать? Можно сделать так, чтобы функция по истечении определённого времени возвращала какой-нибудь результат (Как например это сделано при работе с COM портами)?

Автор: xvr 20.10.2009, 11:33
Цитата(Usper @ 20.10.2009,  09:19)
Сейчас столкнулся с тем, что клиент во время чтения из канала виснет намертво, если сервер ничего не пишет в канал. То есть функция ReadFile() бесконечно ждёт информацию от сервера. Как этого избежать? 

Использовать Overlapped IO или неблокирующий режим открытия Named Pipe'а


Автор: Usper 10.11.2009, 14:11
А можно поподробнее про эти режимы? Сервер ведь может и не ответить вообще, клиент при этом не должен зависать. В этих условиях эти режимы подходят?

Автор: xvr 10.11.2009, 21:07
Цитата(Usper @ 10.11.2009,  14:11)
А можно поподробнее про эти режимы? 

Совершенно обычные режимы Overlapped IO. Любая функция в Overlapped режиме только стартует операцию и немедленно возвращается с ошибкой ERROR_IO_PENDING. Дальше программа может заниматься чем хочет, а когда операция наконец выполнится, то система взведет Event (который подавался как параметр в оригинальную функцию)
Неблокирующий режим просто сразу возвращается с ошибкой, т.е. программа должна периодически пробовать снова и снова

Цитата

Сервер ведь может и не ответить вообще, клиент при этом не должен зависать. В этих условиях эти режимы подходят?
Подходят, но сами по себе эти режимы не обеспечат независание и бесперебойную работу - нужно грамотно сделать дезайн проекта. В этом режимы помогут  smile 

Автор: Usper 11.11.2009, 13:46
А в клиенте надо указать какие-то параметры, указывающие на то, что необходимо работать в неблокирующем режиме? У меня почему-то функция ReadFile всё ещё бесконечно ждёт ответа сервера.

Сервер:
Код
...
void __fastcall TMonPipe::Execute()
{
    char pBuf[BufSize];
    unsigned long cbRead;
    hPipe = CreateNamedPipe("\\\\.\\pipe\\$MonitorPipe$",PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT,
        1, BufSize, BufSize, 1000, NULL);
        if(hPipe==INVALID_HANDLE_VALUE)
        {
            ErrorMessage="Не удалось открыть канал взаимодействия приложений.";
            Synchronize(CallShowMessageFunction);
            return;
        }
    while(!Terminated)
    {
        UpdCapt="Нет коннекта";
        Synchronize(UpdateCaption);
        //if(ConnectNamedPipe(hPipe, NULL))
        ConnectNamedPipe(hPipe, NULL);
        if(GetLastError()==ERROR_PIPE_CONNECTED)
        {
            UpdCapt="Есть коннект";
            Synchronize(UpdateCaption);
            while(!Terminated)
            {
                if(ReadFile(hPipe, pBuf, BufSize, &cbRead, NULL))
                {
                    if(AnsiString(pBuf)=="Test") SendMessage("Test - Ok");

                }
                else
                    if(GetLastError()!=ERROR_NO_DATA)
                    {
                        DisconnectNamedPipe(hPipe);
                        break;
                    }
                Sleep(50);    
            }
         }
         Sleep(50);
         //CloseHandle(hPipe);
    }
}

void __fastcall TMonPipe::SendMessage(AnsiString S)
{
    unsigned long cbWritten;
    WriteFile(hPipe, S.c_str(), S.Length()+1,&cbWritten, NULL);
}
...


Клиент:
Код
...
    hPipe = CreateFile(
    "\\\\192.168.0.4\\pipe\\$MonitorPipe$", GENERIC_READ | GENERIC_WRITE,
    0, NULL, OPEN_EXISTING, 0, NULL);
    if(hPipe==INVALID_HANDLE_VALUE)
    {
        Application->MessageBoxA("Ошибка открытия канала","Ошибка",MB_ICONERROR);
        return;
    }
...
    char szBuf[512];
    unsigned long cbRead;
    WriteFile(hPipe, Edit1->Text.c_str(), Edit1->Text.Length()+1,
      &cbWritten, NULL);
    int counter;
    counter=0;
    while(1)
    {
    counter++;
    if(ReadFile(hPipe, szBuf, 128, &cbRead, NULL))
    {
        Memo1->Lines->Add("Принято:"+AnsiString(szBuf));
        break;
    }
    if(szBuf==0 && counter<20) {Sleep(50); continue;}
    else {Memo1->Lines->Add("Выход по таймауту");break;}
    }
...


Автор: xvr 11.11.2009, 16:21
Цитата(Usper @ 11.11.2009,  13:46)
А в клиенте надо указать какие-то параметры, указывающие на то, что необходимо работать в неблокирующем режиме?

Надо. См. функцию http://msdn.microsoft.com/en-us/library/aa365787%28VS.85%29.aspx

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)