The purpose is to run other programs in Python. Specifically, I wondered if I could use the SDK published in C language in combination with Python.
I adopted this because it is possible to run other executable programs from Python by using the subprocess of the Python module. Communication with other processes includes an interprocess communication method called PIPE, and it feels like it can be done quickly. Actually, it works well with the following combination of Python (control) and C (program to run).
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__":
#Launch the exe file as a process
#Connect stdin and stdout as PIPE to this Python
cmd = ".\\OK.exe"
p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)
#Send instructions to an instance of Popen with communicate
#The return value is(stdout, stderr)Because of the tuple of
#Because b before the character string is converted to byte
out, err = p.communicate(input=b"Hello World")
print(out)
exit()
(Compile OK.cpp and put it as OK.exe in the same folder as control.py below)
However, the SDK I wanted to handle changed state through the input of stdin, and required input many times. In this case, even if you use the same control.py as before, the operation will stop at the communicate part. For example, such a C program is NG.
NG.cpp
#include <stdio.h>
#include <string.h>
#define MAX_BUFFER 256
int main()
{
char buf[MAX_BUFFER], *ret;
//Continue to receive input until there is an end character in the input string
while(1)
{
fgets(buf, MAX_BUFFER, stdin);
printf("your input is :%s", buf);
//Determine if there is an end in the input string
ret = strstr(buf, "end");
//If not, NULL is returned in ret, so the while statement is exited.
if(ret!=NULL)
{
break;
}
}
return 0;
}
I couldn't find an implementation on the Python side to solve this ... so this time I solved it with the implementation on the C side. An implementation example on the C side is shown below, but if anyone knows how to do this on the Python side, I would appreciate it if you could tell me. (I found that pyexpect is a pretty good line, so I actually tried it, but I gave up because I couldn't use the full functionality unless it was Linux. If I do it on Linux, it may be a little less difficult. not)
The method adopted this time is to receive commands via TCP server / client communication and start the client process each time on the Python side. An example is shown below.
main.cpp
#include "stdafx.h"
char *getCharTCP(char*);
int main()
{
char buf[DEFAULT_BUFLEN];
char *ret;
//Continue to receive input until there is an end character in the input string
while(1)
{
//fgets(buf, DEFAULT_BUFLEN, stdin);
printf("waiting new input :\n");
ret = getCharTCP(buf);
printf("your input is :%s", buf);
//Determine if there is an end in the input string
ret = strstr(buf, "end");
//If not, NULL is returned in ret, so the while statement is exited.
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
//Turn off Visual Studio warning
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//include for 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"
(I put the compiled server program and client program in the same folder as Python)
control.py
import subprocess as sp
if __name__ == "__main__":
#Launch the exe file on the server side
#This file is the SDK
cmd = ".\\server.exe"
pserve = sp.Popen(cmd)
#Client-side program
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()
#You need to launch an instance every time you send a command
#I think there is a better implementation
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()
I started thinking that the Python module would be able to handle it immediately, but I was addicted to it unexpectedly, so I wrote it down. I feel that there is a better solution (such as interprocess communication with PIPE), so I may take the opportunity to investigate more. However, since we implemented TCP communication, I think it was a good part that it could be executed from other PCs as a by-product. There may be another good control method using Python's socket module.
OS:Win10 IDE: Visual Studio 2017 (both C and Python were created in VS2017) Python : Anaconda 5.0.1
Python official: subprocess [Caution] when you want to return a character string in a function (https://fa11enprince.hatenablog.com/entry/2014/06/10/023712) Winsock2 implementation example (TCP communication): Getting Started with Winsock2
Recommended Posts