Contrôler d'autres programmes depuis Python (communication entre Python ⇔ exe)

1. Objet

Le but est d'exécuter d'autres programmes en Python. Plus précisément, je me demandais si je pourrais utiliser le SDK publié en langage C en combinaison avec Python.

2. Essayez et erreur

2.1 Quand il peut être contrôlé tel quel par le sous-processus Python

J'ai adopté cela car il est possible d'exécuter d'autres programmes exécutables à partir de Python en utilisant le sous-processus du module Python. La communication avec d'autres processus comprend une méthode de communication inter-processus appelée PIPE, et il semble que cela puisse être fait rapidement. En fait, cela fonctionne bien avec les combinaisons suivantes de Python (contrôle) et C (programme en cours d'exécution).

OK.cpp


#include <stdio.h>

#define MAX_BUFFER 256

int main()
{
    char buf[MAX_BUFFER];

    fgets(buf, MAX_BUFFER, stdin);
    printf("your input is :%s", buf);

    return 0;
}

control.py


import subprocess as sp

if __name__ == "__main__":

    #Lancez le fichier exe en tant que processus
    #Connectez stdin et stdout en tant que PIPE à ce Python
    cmd = ".\\OK.exe"
    p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)

    #Envoyer des instructions à une instance de Popen avec Communiquer
    #La valeur de retour est(stdout, stderr)À cause du tapage de
    #Parce que b avant que la chaîne de caractères soit convertie en octet
    out, err = p.communicate(input=b"Hello World")

    print(out)
    exit()

(Compilez OK.cpp et placez-le comme OK.exe dans le même dossier que control.py ci-dessous)

2.2 Quand il ne peut pas être contrôlé tel quel avec le sous-processus Python (j'en étais accro)

Cependant, le SDK que je voulais gérer a changé d'état via l'entrée de stdin et a nécessité une entrée plusieurs fois. Dans ce cas, même si vous utilisez le même control.py qu'avant, l'opération s'arrêtera au niveau de la partie de communication. Par exemple, un tel programme C est NG.

NG.cpp


#include <stdio.h>
#include <string.h>

#define MAX_BUFFER 256

int main()
{
    char buf[MAX_BUFFER], *ret;
    //Continuer à recevoir des entrées jusqu'à ce qu'il y ait un caractère de fin dans la chaîne de caractères d'entrée
    while(1)
    {
        fgets(buf, MAX_BUFFER, stdin);
        printf("your input is :%s", buf);
        //Déterminez si la chaîne d'entrée a une fin
        ret = strstr(buf, "end");
        //Sinon, NULL est retourné dans ret, donc l'instruction while est quittée.
        if(ret!=NULL)
        {
            break;
        }
    }
    return 0;
}

Je n'ai pas trouvé d'implémentation côté Python pour résoudre cela ... donc cette fois je l'ai résolu avec l'implémentation côté C. Un exemple d'implémentation du côté C est montré ci-dessous, mais si quelqu'un sait comment faire cela du côté Python, j'apprécierais que vous me le disiez. (J'ai trouvé que pyexpect est une très bonne ligne, donc je l'ai essayé, mais j'ai abandonné parce que je ne pouvais pas utiliser toutes les fonctionnalités à moins que ce ne soit Linux. Si je le fais sous Linux, c'est peut-être un peu moins difficile. ne pas)

3. Méthode adoptée: communication de socket

Cette fois, nous avons adopté la communication serveur / client TCP pour recevoir des commandes et démarrer le processus client côté Python à chaque fois. Un exemple est présenté ci-dessous.

main.cpp


#include "stdafx.h"

char *getCharTCP(char*);

int main()
{
    char buf[DEFAULT_BUFLEN];
    char *ret;
    //Continuer à recevoir des entrées jusqu'à ce qu'il y ait un caractère de fin dans la chaîne de caractères d'entrée
    while(1)
    {
        //fgets(buf, DEFAULT_BUFLEN, stdin);
        printf("waiting new input :\n");
        ret = getCharTCP(buf);
        printf("your input is :%s", buf);
        //Déterminez si la chaîne d'entrée a une fin
        ret = strstr(buf, "end");
        //Sinon, NULL est retourné dans ret, donc l'instruction while est quittée.
        if(ret!=NULL)
        {
            break;
        }
    }
    return 0;
}

getCharTCP.cpp


#include "stdafx.h"

char *getCharTCP(char *out)
{
    WSADATA wsaData;
    int iResult, i;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    char errorcode[DEFAULT_BUFLEN];
    strcpy(errorcode, "error");

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return errorcode;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return errorcode;
    }

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return errorcode;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    // No longer need server socket
    closesocket(ListenSocket);

    // Receive until the peer shuts down the connection
    do {

        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // Echo the buffer back to the sender
            iSendResult = send(ClientSocket, recvbuf, iResult, 0);
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return errorcode;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return errorcode;
        }

    } while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return errorcode;
    }

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();
    for (i = strlen(recvbuf) - 1; i >= 0; i--)
    {
        strncpy(out, recvbuf, i + 1);
        out[i + 1] = '\0';
        break;
    }
    printf("receive end\n");
    return out;
}

stdafx.cpp


#include "stdafx.h"

stdafx.h


#pragma once
//Désactiver les avertissements Visual Studio
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>



//inclure pour getCharTCP
#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>

// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

main.cpp


#include "stdafx.h"


int main(int argc, char **argv)
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
        *ptr = NULL,
        hints;
    char sendbuf[DEFAULT_BUFLEN];
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;

    printf("please write letters to control program :\n");
    fgets(sendbuf, DEFAULT_BUFLEN, stdin);

    // Validate the parameters
    if (argc != 2) {
        printf("usage: %s server-name\n", argv[0]);
        return 1;
    }

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        // Connect to server.
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    printf("Bytes Sent: %ld\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0)
            printf("Bytes received: %d\n", iResult);
        else if (iResult == 0)
            printf("Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());

    } while (iResult > 0);

    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return 0;
}

stdafx.h


#pragma once

#include <stdio.h>

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

(J'ai mis le programme serveur compilé et le programme client dans le même dossier que Python)

control.py


import subprocess as sp

if __name__ == "__main__":

    #Lancez le fichier exe côté serveur
    #Ce fichier est le SDK
    cmd = ".\\server.exe"
    pserve = sp.Popen(cmd)

    #Programme côté client
    cmd = ".\\client.exe localhost"
    pclient = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)
    out, err = pclient.communicate(input=b"Hello World 1st\n")
    pclient.kill()
    #Vous devez lancer une instance à chaque fois que vous envoyez une commande
    #Je pense qu'il y a une meilleure mise en œuvre
    cmd = ".\\client.exe localhost"
    pclient = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)
    out, err = pclient.communicate(input=b"Hello World 2nd\n")
    pclient.kill()
    exit()

4. Sentiments divers

J'ai commencé à penser que le module Python serait capable de le gérer immédiatement, mais j'en étais accro de manière inattendue, alors je l'ai écrit. Je pense qu'il existe une meilleure solution (telle que la communication inter-processus avec PIPE), alors je peux profiter de l'occasion pour enquêter davantage. Cependant, depuis que nous avons implémenté la communication TCP, je pense que c'était une bonne partie qu'elle puisse être exécutée à partir d'autres PC en tant que sous-produit. Il peut y avoir une autre bonne méthode de contrôle utilisant le module socket de Python.

a1. Environnement

OS:Win10 IDE: Visual Studio 2017 (C et Python ont été créés dans VS2017) Python : Anaconda 5.0.1

a2. URL du site de référence

Officiel Python: sous-processus [Attention] lorsque vous souhaitez renvoyer une chaîne de caractères dans une fonction (https://fa11enprince.hatenablog.com/entry/2014/06/10/023712) Exemple d'implémentation Winsock2 (communication TCP): Premiers pas avec Winsock2

Recommended Posts

Contrôler d'autres programmes depuis Python (communication entre Python ⇔ exe)
Contrôler d'autres programmes depuis Python (communication entre Python ⇔ exe)
Communication inter-processus entre Ruby et Python (file d'attente de messages POSIX)
La communication I2C est effectuée en contrôlant la communication série avec python (à l'aide d'un périphérique USBGPIO8)
Contrôle de la communication série avec communication python et SPI (à l'aide d'un périphérique USBGPIO8)
Différence entre Ruby et Python Split
Différence entre list () et [] en Python
Différence entre == et est en python
Python, rendement, retour et parfois rendement de
Lire et utiliser des fichiers Python à partir de Python
Étude de Python Hour2: instruction de contrôle
Coopération entre le module python et l'API
Différence entre Python, stftime et strptime
À propos de Python, à partir et à l'importation, comme
Différence entre la série python2 et la série python3 dict.keys ()
Module de socket Python 3 et flux de communication de socket
[Python] Différence entre fonction et méthode
Python - Différence entre exec et eval
[Python] Différence entre randrange () et randint ()
[Python] Différence entre trié et trié (Colaboratoire)
Communiquez entre Elixir et Python avec gRPC
Différence d'authenticité entre Python et JavaScript
De Python à l'utilisation de MeCab (et CaboCha)
Différences entre Ruby et Python dans la portée
Communication socket et traitement multi-thread par Python
différence entre les instructions (instructions) et les expressions (expressions) en Python
Différences entre la syntaxe Python et Java
Différences dans la relation entre PHP et Python enfin et quitter
Différence entre @classmethod et @staticmethod en Python
Différence entre append et + = dans la liste Python
Différence entre non local et global en Python
[Python] Différence entre la méthode de classe et la méthode statique
Portage et modification du solveur de doublets de python2 vers python3.
[Débutant] Installation de Python et exécution de programmes (Windows)
[Python3] Basculer entre Shift_JIS, UTF-8 et ASCII
[Python Iroha] Différence entre List et Tuple
Communication socket par langage C et Python
[python] Différence entre la sortie rand et randn
Différences de multithreading entre Python et Jython
Différence entre Ruby et Python (syntaxe de base)
Correspondance entre les fonctions intégrées de Python et Rust
Communication de données chiffrées entre Python et C #
Étude à partir de Python Lecture et écriture de fichiers Hour9
La réponse de "1/2" est différente entre python2 et 3
[python] Différence entre variable et self. Variable dans la classe
[Python] Comment lire les données de CIFAR-10 et CIFAR-100
[Python] Trouver des coordonnées sous deux angles et une distance
[Python] Mémo de conversion entre les données temporelles et les données numériques
À propos de la différence entre "==" et "is" en python
Jeu manuel Python (interopérabilité entre CSV et PostgreSQL)
Charger et exécuter la commande depuis yml avec python
[Python] Chapitre 02-01 Bases des programmes Python (opérations et variables)
Lier PHP et Python à partir de zéro sur Laravel
Contrôlons les moteurs et capteurs EV3 avec Python
Communication série entre Raspberry pi --Arduino Uno (Python)