Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
 Аватар для Russian_Dragon
10 / 11 / 2
Регистрация: 18.02.2012
Сообщений: 140

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

26.01.2014, 15:35. Показов 3265. Ответов 13
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую.

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

Закрытие именованного канала при завершении работы программы
Добрый день. Пример: int main() { CreateNamedPipe(...); return 0; }

Дан номер телевизионного канала (от 1 до 5).Вывести на экран наиболее популярные программы заданного канала
Дан номер телевизионного канала (от 1 до 5).Вывести на экран наиболее популярные программы заданного канала.

Странное поведение именованного канала
#include "stdafx.h" /* Код внутри stdafx.h: #pragma once #include "targetver.h" #include <iostream> #include <string> ...

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

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

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

Цитата Сообщение от Russian_Dragon Посмотреть сообщение
CancelSynchronousIo(this->pipeServer); DeleteFile("Имя канала тут");
DeleteFile для закрытия канала не нужен.
0
 Аватар для Russian_Dragon
10 / 11 / 2
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 16:50  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Можете привести минимальный пример, воспроизводящий проблему ?
Просто в работе с каналами масса нюансов: как оба потока открывают
канал - через 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 минуты
Цитата Сообщение от Убежденный Посмотреть сообщение
Что значит "не выходит" ?
Мы всё равно висим на функции чтения данных их канала.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
26.01.2014, 16:51
Минимальный код, воспроизводящий проблему - тот, который можно
загнать копи-пастом в Visual Studio, скомпилировать и получить
наблюдаемый результат. По-другому я вряд ли смогу помочь.
0
 Аватар для Russian_Dragon
10 / 11 / 2
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 17:28  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Минимальный код, воспроизводящий проблему - тот, который можно
загнать копи-пастом в Visual Studio, скомпилировать и получить
наблюдаемый результат. По-другому я вряд ли смогу помочь.
С этим большая проблема.
Эх, спасибо и на этом. Попробую сам поколдовать, может и найду в чем фокус.

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

Правда я не понял каким боком она заработала, если я на серверной стороне использую ReadFile(...). Возможно, клиент, реализуемый на C#, использует именно ReadFileEx(...), из-за этого CancelIo(this->pipeServer); и не срабатывал, хотя и ошибку тоже не выдавал.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
26.01.2014, 17:52
Все дело в том, что использовать один и тот же хэндл из разных потоков в
общем случае небезопасно. Например, что будет, если поток А все время
будет звать 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 и выше...
0
 Аватар для Russian_Dragon
10 / 11 / 2
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 18:04  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
CancelIo отменяет только те операции, которые были инициированы текущим потоком.
Вот почему ее использование очень ограничено. А у CancelIoEx такого недостатка нет.
Правда, доступна она только в Vista и выше...
Вопрос на засыпку, а как можно с одного потока вызвать ReadFile, а после CancelIo ?
Или чего-то я не понимаю?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
26.01.2014, 18:09
Цитата Сообщение от Russian_Dragon Посмотреть сообщение
Вопрос на засыпку, а как можно с одного потока вызвать ReadFile, а после CancelIo ?
ReadFile может быть вызвана в асинхронном режиме.
0
 Аватар для Russian_Dragon
10 / 11 / 2
Регистрация: 18.02.2012
Сообщений: 140
26.01.2014, 18:10  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Все дело в том, что использовать один и тот же хэндл из разных потоков в
общем случае небезопасно. Например, что будет, если поток А все время
будет звать ReadFile, а поток Б - WriteFile ?
В моей архитектуре, таково не должно быть по определению. Чтение и запись в канал осуществляется с одного потока.

Не по теме:

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


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

Добавлено через 1 минуту
Цитата Сообщение от Убежденный Посмотреть сообщение
Ну для этого и придумали CancelIoEx.
Если CancelIoEx недоступна, т.е. мы в XP/Server2003, тогда ничего другого не
остается, кроме как отправить сообщение потоку, который инициировал
операцию, чтобы он вызвал CancelIo. Операция, естественно, должна быть
асинхронной.
Угу. Спасибо, за пояснения, нюансов работы с каналами.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
26.01.2014, 18:17
Асинхронный I/O может быть очень разным.
Например, вы можете в одном потоке запустить десяток операций, а затем
ждать, пока хотя бы одна из них завершится. Потом выполнить обработку,
запустить новую порцию операций и снова ждать. И так далее.
А можно запустить целый ворох потоков и каждому раздать пачку хэндлов,
открытых в асинхронном режиме, пущай работают... А можно эти хэндлы
связать с портом завершения ввода-вывода, а пул потоков заставить выгребать
из него завершенные операции и заниматься их обработкой...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
26.01.2014, 18:17
Помогаю со студенческими работами здесь

WCF принудительный разрыв подлючения
Приветствую вас. Проблема в следующем есть дуплексный канал когда клиент подключен к серверу при некорректном завершении приложения...

Принудительный разрыв интернет-соединения
приве всем. Есть вопросик, как можно принудительно разорвать интернет соединение? Добавлено через 1 час 7 минут есть процедура: //...

Передача массива с помощью именованного канала от сервера клиенту
Сервер создает и печатает массива 10 целых случайных чисел в диапазоне от минус 50 до 50. С помощью именованного канала передачи сервер...

Как сделать принудительный перенос (разрыв) слова, если оно не умещается в блоке?
Т.е. когда есть длинное слово без пробелов, типа &quot;авмолтеколтпннолитинолиетниолмтншоин6тишо&quot; и блок небольшой щирины. Вот...

Поиск именованного диапазона
Всем привет. Подскажите, пожалуйста, у меня есть несколько именованных диапазонов в столбце А (Например А1:А10 - Тест1, А11:А20 -...


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

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru