web-dev-qa-db-fra.com

Python et Windows Named Pipes

Quelle est la bonne façon de communiquer avec les pipes nommés sous Windows à partir de Python? Je l'ai googlé, et je ne trouve aucun paquet qui enveloppe cette communication.

Il y a: 

J'ai juste besoin de me connecter à un canal nommé existant et de le lire/écrire. Auparavant, je n'avais essayé que la communication avec le port série (à l'aide de pySerial) et je suis étonné du peu d'informations que je pouvais trouver sur les canaux nommés par rapport à celui-ci. Il y a généralement des tonnes de guides pour tout usage de Python.

Je vais apprécier toute aide.

7
Ray P.

Pour vous connecter à un canal nommé nommé existant, vous pouvez utiliser l'API CreateFile fournie par le package pywin32. Comme il m'a fallu un certain temps pour constituer une base de travail, voici un exemple client/serveur qui me convient (python 3.6.5, pywin32 223 sur Windows 10 Pro x64):

import time
import sys
import win32pipe, win32file, pywintypes


def pipe_server():
    print("pipe server")
    count = 0
    pipe = win32pipe.CreateNamedPipe(
        r'\\.\pipe\Foo',
        win32pipe.PIPE_ACCESS_DUPLEX,
        win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
        1, 65536, 65536,
        0,
        None)
    try:
        print("waiting for client")
        win32pipe.ConnectNamedPipe(pipe, None)
        print("got client")

        while count < 10:
            print(f"writing message {count}")
            # convert to bytes
            some_data = str.encode(f"{count}")
            win32file.WriteFile(pipe, some_data)
            time.sleep(1)
            count += 1

        print("finished now")
    finally:
        win32file.CloseHandle(pipe)


def pipe_client():
    print("pipe client")
    quit = False

    while not quit:
        try:
            handle = win32file.CreateFile(
                r'\\.\pipe\Foo',
                win32file.GENERIC_READ | win32file.GENERIC_WRITE,
                0,
                None,
                win32file.OPEN_EXISTING,
                0,
                None
            )
            res = win32pipe.SetNamedPipeHandleState(handle, win32pipe.PIPE_READMODE_MESSAGE, None, None)
            if res == 0:
                print(f"SetNamedPipeHandleState return code: {res}")
            while True:
                resp = win32file.ReadFile(handle, 64*1024)
                print(f"message: {resp}")
        except pywintypes.error as e:
            if e.args[0] == 2:
                print("no pipe, trying again in a sec")
                time.sleep(1)
            Elif e.args[0] == 109:
                print("broken pipe, bye bye")
                quit = True


if __== '__main__':
    if len(sys.argv) < 2:
        print("need s or c as argument")
    Elif sys.argv[1] == "s":
        pipe_server()
    Elif sys.argv[1] == "c":
        pipe_client()
    else:
        print(f"no can do: {sys.argv[1]}")

Exemple de client de sortie

> python pipe_test.py c
pipe client
no pipe, trying again in a sec
no pipe, trying again in a sec
no pipe, trying again in a sec
message: (0, b'0')
message: (0, b'1')
message: (0, b'2')
message: (0, b'3')
message: (0, b'4')
message: (0, b'5')
message: (0, b'6')
message: (0, b'7')
message: (0, b'8')
message: (0, b'9')
broken pipe, bye bye

Exemple de serveur de sortie

> python pipe_test.py s
pipe server
waiting for client
got client
writing message 0
writing message 1
writing message 2
writing message 3
writing message 4
writing message 5
writing message 6
writing message 7
writing message 8
writing message 9
finished now

Évidemment, vous auriez besoin d’une vérification d’erreur lors des différents appels, mais cela devrait fonctionner.

Remarque supplémentaire: un de mes collègues a eu des problèmes avec la fermeture du canal au moment où le client a tenté d’effectuer des E/S dessus (exception prétendant que "toutes les instances de canal sont occupées"). Il s'est avéré qu'il utilisait os.path.exists dans le code client pour vérifier si le canal nommé existait déjà avant d'exécuter CreateFile sur celui-ci. Cela brise en quelque sorte le tuyau. Ainsi, l’approche ci-dessus (CreateFile encapsulée dans une liste try-except) constitue le moyen sûr d’essayer de se connecter à un canal jusqu’à ce qu’il ait été créé par le serveur.

6
ChrisWue

J'ai du succès avec quelque chose comme le fragment suivant. Ce code est dérivé de CaptureSetup/Pipes - Python sous Windows - Le wiki Wireshark. Il nécessite win32pipe et win32file à partir de pywin32 package.

# pipename should be of the form \\.\pipe\mypipename
pipe = win32pipe.CreateNamedPipe(
        pipename,
        win32pipe.PIPE_ACCESS_OUTBOUND,
        win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
        1, 65536, 65536,
        300,
        None)
try:
    win32pipe.ConnectNamedPipe(pipe, None)

    while True:
        some_data = b'12345...'
        win32file.WriteFile(pipe, some_data)
        ...
finally:
    win32file.CloseHandle(pipe)

Je ne sais pas si c'est correct à 100% dans la façon dont il ferme le tuyau.

Vous avez mentionné Les périls et les triomphes d'être un geek: des tubes nommés entre C # et Python - Jonathon Reinhart. Je l'ai essayé, mais il n'a pas été possible de créer le tuyau nommé. Je me demande si ce code ne fonctionne que pour ouvrir un canal nommé qui a déjà été créé par un autre processus.

0
Craig McQueen