Именованный канал Windows: обнаружение в Python на стороне пишущего, когда читатель закрыл свой конец без необходимости записи данных - PullRequest
1 голос
/ 10 апреля 2019

Мое приложение передает (записывает) данные в именованный канал с именем канала, указанным в качестве аргумента CLI при вызове приложения. Потоковые данные нерегулярны с фазами, где не может быть никаких данных для отправки по каналу.

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

Поскольку формат данных потока фиксирован и не допускает пустых операций записи или проверки связи, я не могу просто попытаться записать некоторые данные канала, даже если у меня нет ничего для потоковой передачи, чтобы посмотреть, продолжает ли считыватель канала читать.

У меня есть рабочее решение для Linux, которое, к сожалению, не работает в Windows, потому что именованные каналы Windows не могут быть одинаково обработаны в select (), как в Linux. В Linux я просто проверяю, доступен ли для чтения конец канала для чтения, поскольку это сигнализирует об ошибке канала, а затем закрываю канал и освобождаю мои выделенные ресурсы.

В Windows это невозможно. Я открыл канал для записи в таком виде:

fifo = open('//./pipe/somepipe', 'wb')

Попытка fifo.read() из канала не работает (как и следовало ожидать) и немедленно выдает OSException.

Как я уже сказал, я не могу попробовать какую-нибудь пустую / нулевую запись; fifo.write(b'') ничего не делает, даже не копает трубку для возможности записи.

Есть ли в Windows какой-либо способ проверить конец записи именованного канала, чтобы убедиться, что ридер (клиент) все еще подключен?

1 Ответ

1 голос
/ 13 апреля 2019

Как @eryksun указывал выше, на самом деле можно написать пробную строку нулевой длины, используя Win32 API WriteFile напрямую: если канал закрыт, то это вернет «безуспешно» (false / 0 ), если труба жива, значит «успех» (true /! = 0). Именно то, что я просил. Поскольку я считаю, что это проще, чем использовать NtQueryInformationFile, я сейчас использую пустой метод записи; вот упрощенный пример:

import ctypes
from ctypes import byref, c_ulong, c_char_p
import msvcrt
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

fifo = open('//./pipe/somepipe', 'wb')
data = b''
written = c_ulong(0)
if not kernel32.WriteFile(
        msvcrt.get_osfhandle(fifo.fileno()),
        c_char_p(data), 0, 
        byref(written), 
        None):
    last_error = ctypes.get_last_error()
    if last_error in (
            0x000000E8,  # ERROR_NO_DATA
            # enable as required: 0x000000E9,  # ERROR_PIPE_NOT_CONNECTED
    ):
        # pipe broken
        pass
    else:
        # something else is wrong...
        pass
else:
    # pipe still okay
    pass

Полезные ресурсы:

...