Форум программистов, компьютерный форум CyberForum.ru

Принудительный разрыв именованного канала - C++

Восстановить пароль Регистрация
 
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 15:35     Принудительный разрыв именованного канала #1
Приветствую.

Есть группа многопоточных приложений которые обмениваются между собой информацией через именованные каналы. Приложения полностью не зависимы с способны восстанавливать соединения при падении одного из них.
Суть проблемы в следующем - при попытки "штатно" закрыть приложение, мне нужно разорвать соединение, т.к. одна из ключевых проверок, является проверка работы потока который читает/пишет данные в канал.
Суть в том, что функции:
C++
1
2
DisconnectNamedPipe(this->pipeServer);
CloseHandle(this->pipeServer);
Не отрабатывают, если в текущий момент, в другом потоке, ожидается чтение данных из канала
C++
1
ReadFile(this->PipeServer(), binaryData, sizeData, &cbRead, NULL)
Как принудительно разорвать канал связи?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.01.2014, 15:35     Принудительный разрыв именованного канала
Посмотрите здесь:

C++ Поля структуры: код телевизионного канала, название, цена. Операция: найти самый дорогой канал
Delphi Принудительный разрыв интернет-соединения
Принудительный выход из программы C++
C++ Дан номер телевизионного канала (от 1 до 5).Вывести на экран наиболее популярные программы заданного канала
Принудительный выход из цикла C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 15:59     Принудительный разрыв именованного канала #2
Делать DisconnectNamedPipe канала в одном потоке, в то время как другой
ждет завершения ReadFile на нем - не очень хорошая идея.
Я бы переделал все на асинхронный лад. Тогда для отмены можно было бы
использовать функцию CancelIo(Ex), да и читающий поток мог бы легко
завершить работу, не дожидаясь, пока придут данные.
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 16:13  [ТС]     Принудительный разрыв именованного канала #3
Цитата Сообщение от Убежденный Посмотреть сообщение
Делать DisconnectNamedPipe канала в одном потоке, в то время как другой
ждет завершения ReadFile на нем - не очень хорошая идея.
А по сути использовать не получается, поток замерает на DisconnectNamedPipe.

Цитата Сообщение от Убежденный Посмотреть сообщение
Я бы переделал все на асинхронный лад. Тогда для отмены можно было бы
использовать функцию CancelIo(Ex), да и читающий поток мог бы легко
завершить работу, не дожидаясь, пока придут данные.
И так не выходит. Пробовал следующую связку функций
C++
1
2
CancelSynchronousIo(this->pipeServer);
DeleteFile("Имя канала тут");
Поток на них не замерает, но и к желаемому результату тоже не приводят. Т.е. второй поток так и висит на чтении из именованного канала.
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 16:42     Принудительный разрыв именованного канала #4
Можете привести минимальный пример, воспроизводящий проблему ?
Просто в работе с каналами масса нюансов: как оба потока открывают
канал - через CreateNamedPipe/CreateFile или через передачу хэндлов,
или через DuplicateHandle(Ex), какие флаги при создании указываются,
особенности синхронного/асинхронного I/O и т.д.

Цитата Сообщение от Russian_Dragon Посмотреть сообщение
И так (на асинхронный лад) не выходит.
Что значит "не выходит" ?

Цитата Сообщение от Russian_Dragon Посмотреть сообщение
CancelSynchronousIo(this->pipeServer); DeleteFile("Имя канала тут");
DeleteFile для закрытия канала не нужен.
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 16:50  [ТС]     Принудительный разрыв именованного канала #5
Цитата Сообщение от Убежденный Посмотреть сообщение
Можете привести минимальный пример, воспроизводящий проблему ?
Просто в работе с каналами масса нюансов: как оба потока открывают
канал - через CreateNamedPipe/CreateFile или через передачу хэндлов,
или через DuplicateHandle(Ex), какие флаги при создании указываются,
особенности синхронного/асинхронного I/O и т.д.
Попробуем.
На сервере (C++)
C++
1
2
this->pipeServer = CreateNamedPipe(GlobalConst::GetNamePiperEncephalomanager().c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 0, 0, NMPWAIT_WAIT_FOREVER, NULL);
BOOL isConnected = ConnectNamedPipe(this->pipeServer, NULL);
Из того же потока (собственно тут и зависаем):
C++
1
ReadFile(this->PipeServer(), binaryData, sizeData, &cbRead, NULL)

На клиенте (С#)
C#
1
2
3
PipeClient = new NamedPipeClientStream(".", "BridgeDataCenter", PipeDirection.InOut);
PipeClient.Connect();
PipeClient.ReadMode = PipeTransmissionMode.Message;
Разорвать соединение пытаемся на сервере.

Добавлено через 2 минуты
Цитата Сообщение от Убежденный Посмотреть сообщение
Что значит "не выходит" ?
Мы всё равно висим на функции чтения данных их канала.
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 16:51     Принудительный разрыв именованного канала #6
Минимальный код, воспроизводящий проблему - тот, который можно
загнать копи-пастом в Visual Studio, скомпилировать и получить
наблюдаемый результат. По-другому я вряд ли смогу помочь.
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 17:28  [ТС]     Принудительный разрыв именованного канала #7
Цитата Сообщение от Убежденный Посмотреть сообщение
Минимальный код, воспроизводящий проблему - тот, который можно
загнать копи-пастом в Visual Studio, скомпилировать и получить
наблюдаемый результат. По-другому я вряд ли смогу помочь.
С этим большая проблема.
Эх, спасибо и на этом. Попробую сам поколдовать, может и найду в чем фокус.

Добавлено через 31 минуту
Методом научного тыка, для моего случая подошла функция
CancelIoEx(this->pipeServer, NULL);

Правда я не понял каким боком она заработала, если я на серверной стороне использую ReadFile(...). Возможно, клиент, реализуемый на C#, использует именно ReadFileEx(...), из-за этого CancelIo(this->pipeServer); и не срабатывал, хотя и ошибку тоже не выдавал.
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 17:52     Принудительный разрыв именованного канала #8
Все дело в том, что использовать один и тот же хэндл из разных потоков в
общем случае небезопасно. Например, что будет, если поток А все время
будет звать ReadFile, а поток Б - WriteFile ? Система должна как-то
синхронизировать эти обращения к общему объекту, в данном примере это файл.
То есть, сначала будет выполнена ReadFile, потом WriteFile, или наоборот.

Если один поток запустил длительную I/O-операцию и ждет ее завершения, а
второй сразу же после этого вызвал, к примеру, DisconnectNamedPipe, то
ему придется подождать своей очереди. Вот почему в Вашем примере она не
отрабатывает как положено, то есть не разрывает соединение сразу.

Вот, набросал такой примерчик:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <string>
#include <Windows.h>
#include <process.h>
 
 
 
using namespace std;
 
 
 
// #define ABORT_PIPE
 
 
 
unsigned int _stdcall Client_ThreadProc(void * /* pParam */)
{
    cout << "Client: wait 2 seconds..." << endl;
 
    Sleep(2000);
 
    cout << "Client: opening pipe..." << endl;
 
    HANDLE const hPipe = CreateFileW(L"\\\\.\\pipe\\MyPipe-12345",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);
 
    if (INVALID_HANDLE_VALUE == hPipe)
    {
        DWORD const GLE = GetLastError();
        cerr << "Client: CreateFileW failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    cout << "Client: wait 4 seconds..." << endl;
    Sleep(4000);
 
    cout << "Client: writting into pipe..." << endl;
 
    DWORD nBytes;
 
    if (FALSE == WriteFile(hPipe, "Hello", 6, &nBytes, NULL))
    {
        DWORD const GLE = GetLastError();
        cerr << "Client: WriteFile failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    cout << "Client: write completed, exiting..." << endl;
 
    return 0;
}
 
 
 
unsigned int _stdcall Stopper_ThreadProc(void *pParam)
{
    HANDLE const hPipe = (HANDLE const)pParam;
 
    cout << "Stopper: wait 4 seconds..." << endl;
 
    Sleep(4000);
 
    cout << "Stopper: disconnecting pipe..." << endl;
    
    if (FALSE == DisconnectNamedPipe(hPipe))
    {
        DWORD const GLE = GetLastError();
        cerr << "Stopper: DisconnectNamedPipe failed with last error " << GLE << "." << endl;
        return 0;
    }    
 
    cout << "Stopper: exiting..." << endl;
 
    return 0;
}
 
 
 
int main()
{
    cout << "main: creating pipe..." << endl;
 
    HANDLE const hPipe = CreateNamedPipeW(L"\\\\.\\pipe\\MyPipe-12345",
        PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES,
        4096, // Output buffer size.
        4096, // Input buffer size.
        NMPWAIT_USE_DEFAULT_WAIT,
        NULL // Security attributes
        );
 
    if (INVALID_HANDLE_VALUE == hPipe)
    {
        DWORD const GLE = GetLastError();
        cerr << "main: CreateNamedPipeW failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    _beginthreadex(NULL, 0, Client_ThreadProc, NULL, 0, NULL);
 
    #ifdef ABORT_PIPE
    _beginthreadex(NULL, 0, Stopper_ThreadProc, hPipe, 0, NULL);
    #endif
 
    if (FALSE == ConnectNamedPipe(hPipe, NULL))
    {
        DWORD const GLE = GetLastError();
        cerr << "main: ConnectNamedPipe failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    cout << "main: client connected..." << endl;
 
    char pBuffer[100];
    DWORD nBytes;
 
    if (FALSE == ReadFile(hPipe, pBuffer, 100, &nBytes, NULL))
    {
        DWORD const GLE = GetLastError();
        cerr << "main: ReadFile failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    cout << "main: read completed [msg = " << pBuffer << "], exiting..." << endl;
 
    if (FALSE == DisconnectNamedPipe(hPipe))
    {
        DWORD const GLE = GetLastError();
        cerr << "main: DisconnectNamedPipe failed with last error " << GLE << "." << endl;
        return 0;
    }
 
    CloseHandle(hPipe);
 
    return 0;
}


Здесь происходит следующее: main создает именованный канал, а затем ожидает
входящих подключений, запуская второй поток, имитирующий работу клиента.
Клиент выдерживает небольшую паузу (2 секунды), затем подключается к каналу,
потом снова выдерживает паузу (4 секунды), записывает сообщение в канал и
завершает работу. main отображает сообщение и тоже завершается.

Если раскомментировать макрос ABORT_PIPE, в игру входит третий поток.
Он зовет DisconnectNamedPipe после того, как клиент подключается к каналу, но
до того, как тот успеет отправить сообщение. Что интересно, на связи клиента и
сервера это почти никак не отображается - клиент все равно отправляет сообщение,
который main успешно принимает и отображает в консоли. Что косвенным образом
подтверждает рассуждения выше о внутренних системных очередях и синхронизации.
А вот DisconnectNamedPipe в main завершается, как и следовало ожидать, ошибкой, так
как функция уже была вызвана в другом потоке, просто выполнена она была не сразу.

Цитата Сообщение от Russian_Dragon Посмотреть сообщение
Методом научного тыка, для моего случая подошла функция
CancelIoEx(this->pipeServer, NULL);
Правда я не понял каким боком она заработала, если я на серверной стороне использую ReadFile(...). Возможно, клиент, реализуемый на C#, использует именно ReadFileEx(...), из-за этого CancelIo(this->pipeServer); и не срабатывал, хотя и ошибку тоже не выдавал.
CancelIo отменяет только те операции, которые были инициированы текущим потоком.
Вот почему ее использование очень ограничено. А у CancelIoEx такого недостатка нет.
Правда, доступна она только в Vista и выше...
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 18:04  [ТС]     Принудительный разрыв именованного канала #9
Цитата Сообщение от Убежденный Посмотреть сообщение
CancelIo отменяет только те операции, которые были инициированы текущим потоком.
Вот почему ее использование очень ограничено. А у CancelIoEx такого недостатка нет.
Правда, доступна она только в Vista и выше...
Вопрос на засыпку, а как можно с одного потока вызвать ReadFile, а после CancelIo ?
Или чего-то я не понимаю?
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 18:09     Принудительный разрыв именованного канала #10
Цитата Сообщение от Russian_Dragon Посмотреть сообщение
Вопрос на засыпку, а как можно с одного потока вызвать ReadFile, а после CancelIo ?
ReadFile может быть вызвана в асинхронном режиме.
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 18:10  [ТС]     Принудительный разрыв именованного канала #11
Цитата Сообщение от Убежденный Посмотреть сообщение
Все дело в том, что использовать один и тот же хэндл из разных потоков в
общем случае небезопасно. Например, что будет, если поток А все время
будет звать ReadFile, а поток Б - WriteFile ?
В моей архитектуре, таково не должно быть по определению. Чтение и запись в канал осуществляется с одного потока.

Не по теме:

Если честно, то я сам путаюсь и не могу себе представить ситуации, если одновременно читать и писать данные в канал на одной стороне, и читать/писать данные на другой. По любому что-то не срастется в итоге.


Но при этом мне нужна была возможность закрыть приложение с команды введенной в консольную строку, а это, естественно, третий поток. Вот и стал мучатся с разрывом.
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 18:14     Принудительный разрыв именованного канала #12
Цитата Сообщение от Russian_Dragon Посмотреть сообщение
Но при этом мне нужна была возможность закрыть приложение с команды введенной в консольную строку, а это, естественно, третий поток. Вот и стал мучатся с разрывом.
Ну для этого и придумали CancelIoEx.
Если CancelIoEx недоступна, т.е. мы в XP/Server2003, тогда ничего другого не
остается, кроме как отправить сообщение потоку, который инициировал
операцию, чтобы он вызвал CancelIo. Операция, естественно, должна быть
асинхронной.
Russian_Dragon
 Аватар для Russian_Dragon
10 / 11 / 0
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 18:15  [ТС]     Принудительный разрыв именованного канала #13
Цитата Сообщение от Убежденный Посмотреть сообщение
ReadFile может быть вызвана в асинхронном режиме.
А, но тогда синхронизация потоков вообще целая головная боль будет... хотя... там есть функция которая выполняется после завершения передачи данных... Да, всё интересней и интересней, но чем дальше тем синхронизация действий сложнее.

Добавлено через 1 минуту
Цитата Сообщение от Убежденный Посмотреть сообщение
Ну для этого и придумали CancelIoEx.
Если CancelIoEx недоступна, т.е. мы в XP/Server2003, тогда ничего другого не
остается, кроме как отправить сообщение потоку, который инициировал
операцию, чтобы он вызвал CancelIo. Операция, естественно, должна быть
асинхронной.
Угу. Спасибо, за пояснения, нюансов работы с каналами.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.01.2014, 18:17     Принудительный разрыв именованного канала
Еще ссылки по теме:

Принудительный выход из цикла C++
Вывести на экран наиболее популярные программы заданного канала C++
Принудительный вызов метода родителя C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Убежденный
Системный программист
 Аватар для Убежденный
14188 / 6203 / 984
Регистрация: 02.05.2013
Сообщений: 10,331
Завершенные тесты: 1
26.01.2014, 18:17     Принудительный разрыв именованного канала #14
Асинхронный I/O может быть очень разным.
Например, вы можете в одном потоке запустить десяток операций, а затем
ждать, пока хотя бы одна из них завершится. Потом выполнить обработку,
запустить новую порцию операций и снова ждать. И так далее.
А можно запустить целый ворох потоков и каждому раздать пачку хэндлов,
открытых в асинхронном режиме, пущай работают... А можно эти хэндлы
связать с портом завершения ввода-вывода, а пул потоков заставить выгребать
из него завершенные операции и заниматься их обработкой...
Yandex
Объявления
26.01.2014, 18:17     Принудительный разрыв именованного канала
Ответ Создать тему
Опции темы

Текущее время: 15:51. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru