python - 使用套接字从 Python(客户端)向 C++(服务器)数组发送数据

标签 python c++ arrays sockets

我一直在尝试使用套接字将数组从 Python 发送到 C++,但一直遇到问题。

Python 方面存在直接发送数组的问题,例如 pickle 与 C++ 不兼容,因此我发现的唯一半可靠的方法是将其作为字符串发送:

import socket
import sys
import random

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('localhost', 5555)
print >>sys.stderr, 'connecting to %s port %s' % server_address
sock.connect(server_address)

# Message to be sent to C++
# message = [random.randint(1, 10),random.randint(1, 10),random.randint(1, 10)]
i = 0

while i < 5:
    a_converted = (random.randint(1,255), random.randint(1,255), random.randint(1,255))
    #a_converted = 'words'

    print a_converted
    # Sending message to C++
    sock.sendall(str(a_converted))
    i += 1

sock.close()

将它作为字符串发送的问题是我实际上需要它作为另一侧的 double 组。我目前拥有的 C++ 代码如下:

#include "stdafx.h"
#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h> //for SOCKET communication
#include <sstream>
#include <stdlib.h>

//Linking Ws2_32.lib, Mswsock.lib, Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#pragma warning(disable:4996)//to disable warning message

using namespace std;

int main()
{
    WSADATA WSAData;

    SOCKET server, client;

    SOCKADDR_IN serverAddr, clientAddr;

    WSAStartup(MAKEWORD(2, 0), &WSAData);
    server = socket(AF_INET, SOCK_STREAM, 0);

    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(5555);
    ::bind(server, (SOCKADDR *)&serverAddr, sizeof(serverAddr));
    listen(server, 0);

    cout << "Listening for incoming connections..." << endl;

    string a_converted[1000];

    int clientAddrSize = sizeof(clientAddr);
    if ((client = accept(server, (SOCKADDR *)&clientAddr, &clientAddrSize)) != INVALID_SOCKET)
    {
        cout << "Client connected!" << endl;
        // Loop 
        int i = 0;

        while (i<5) {

            recv(client, (char*)a_converted, sizeof(a_converted), 0);

            char char_array[sizeof(a_converted)];

            strcpy(char_array, (char*)a_converted);
            memset(a_converted, 0, sizeof(a_converted));
            cout << "Client says: " << char_array << endl; 
            cout << endl;

            i = i++;

        }
        closesocket(client);
        WSACleanup();
        cout << "Client disconnected." << endl;
    }
    cout << "Press Enter to continue" << endl;
    getchar();
}

信息已收到且正确,但我一直无法正确转换数据。我曾尝试使用 atof 和类似的函数在 C++ 端进行转换,但 Python 端存在逗号和括号似乎会导致它出错并给出零,而且我在尝试将它们从字符串中删除时运气不佳。

我忍不住认为一定有更好的方法来做到这一点,但我真的是编码新手,所以如果我忽略了某些东西,我不会感到惊讶。 如果能解释如何直接发送 C++ 可以从 Python 读取的数组,或者转换它在 C++ 中发送的字符串的方法,我将不胜感激。

最佳答案

实现此目的最直接的方法是使用 python 的 struct 模块将您的数组编码为便于在 C++ 中接收的二进制格式。

例如,要发送一个 32 位整数数组,您可以这样做:

import struct

def encode_int_array(int_list):
    buf = struct.pack("!I" + "I" * len(int_list), len(int_list), *int_list)
    return buf

说明:! 字符指定编码中使用的字节顺序(此处为大端/“网络”顺序),I 字符是此处用于数组长度,然后对每个要编码的整数再次使用一次。然后打包实际数组长度和每个整数。

因此,如果您使用列表 [1, 2, 3] 调用此函数,则提供给 pack 的格式字符串将为 "!IIII",其余参数将是 3, 1, 2, 3(第一个“3”是要编码的数组长度)。最终结果是包含编码的 32 位(4 字节)整数的 bytes 字符串:

|ArrayLen|Integer0|Integer1|Integer2|....

将上面的代码与 sendall 一起使用以传输生成的缓冲区:

sock.sendall(encode_int_array(
    [random.randint(1,255), random.randint(1,255), random.randint(1,255)]))

在 C++ 端,首先读取 4 个字节(以获取数组长度),将数组长度转换为 native 字节顺序,然后读取额外的 4 * array-length 字节以获取所有整数;然后将它们中的每一个转换为 native 字节顺序。你应该小心,永远不要假设 recv 会收到你想要的所有数据。 SOCK_STREAM 语义不能保证这一点。因此,您需要确保收到的数量与您预期的完全一致。

C++ 端可能看起来像这样:

#include <cstdint>  // uint32_t et al definitions

// Function to receive exactly "len" bytes.
// Returns number of bytes received, or -1 on EOF or error
int recv_all(int sock, char *buf, unsigned int len)
{
    unsigned int n = 0;
    int status;
    while (n < len) {
        status = recv(sock, buf + n, len - n);
        if (status == 0) {
            // Unexpected End of File
            return -1; // Or whatever
        } else if (status < 0) {
            // Error
            return -1; // Need to look at errno to find out what happened
        } else {
            n += status;
        }
     }
     return (int)n;
}

...
int status;
// Receive array length from client
uint32_t array_len;
status = recv_all(client, reinterpret_cast<char *>(&array_len), sizeof array_len);
if (status < 0) {
    // handle error
}

array_len = ntohl(array_len); // Convert length to native byte order

// Receive array contents from client
uint32_t int_array[array_len];
status = recv_all(client, reinterpret_cast<char *>(&int_array[0]), sizeof int_array);
if (status < 0) {
    // handle error
}
for (unsigned int n = 0; n < array_len; ++n)
    int_array[n] = ntohl(int_array[n]); // Convert to native byte order

(如果您只想发送单字节整数,请在上面的 pack 调用中将 'B' 替换为 'I',并且下面的 C++ 也需要相应地进行调整——比如用 uint8_t 代替 uint32_t。)

关于python - 使用套接字从 Python(客户端)向 C++(服务器)数组发送数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48867997/

相关文章:

python - 使用 selenium 运行 django-test 时管道错误

python - 当链接没有类时,使用 Xpath 在 Python 中获取链接的 anchor 文本

C++/CX : Converting from IVector<T>^ to Vector<T>^

c++ - 为什么我的空数组不为空?

arrays - 具有通用项的 Swift 数组扩展

python - wxpython 的 wxEvent.Skip() 解释

python - 如何使用 Python CFFI 正确包装 C 库

c++ - 如何将分配在堆上的仿函数的 operator()() 传递给线程?

javascript - 如何将数组中的对象作为带逗号的字符串返回?

arrays - 从解码的 Firebase 数据中创建一个数组