使用从Python(客户端)到C ++(服务器)阵列的套接字发送数据

时间:2018-02-19 14:09:54

标签: 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()

将其作为字符串发送的问题是我实际上需要它作为另一侧的双样式数组。我目前的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();
}

收到的信息正确但我无法正确转换数据。我试图在C ++端使用atof和类似的函数进行转换,但是Python端的逗号和括号的出现似乎导致它出错并给出零并且我没有运气试图将它们从字符串。

我无能为力,但我认为必须有更好的方法来做到这一点,但我对编码很陌生,所以如果我忽略了某些东西就不会感到惊讶。 我希望能够解释如何直接发送C ++可以从Python读取的数组,或者转换它在C ++中发送的字符串的方式。

3 个答案:

答案 0 :(得分:0)

这听起来像是序列化问题。由于您想连接两种不同的语言,我建议使用标准化的,众所周知的格式,如JSON或XML。有许多用于将JSON或XML转换为对象的库,反之亦然。

终极,死端解决方案是将数据打包成二进制文件并通过套接字发送。首先尝试外部库以获取JSON或XML

修改

JSON(以非常简单的方式)描述了如何将对象保存为文本(序列化)。它就像

一样简单
{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
  "children": [],
  "spouse": null
}

(摘自https://en.wikipedia.org/wiki/JSON

您可以想象,从文本再次读取对象(反序列化)是一个非常直接的过程。那里有图书馆可以为你完成这项工作。这意味着您的项目:

  • 找到一个python JSON库,将您的对象序列化为JSON格式
  • 找到一个将数据反序列化为对象的c ++ JSON库

我已经看到这个在使用中,因为每个数据类型都被视为一个字符串来发送它,并且在反序列化方面,你需要为每种情况确定它实际上是哪种数据类型(将字符串转换为double或int或漂浮或布尔等。)

答案 1 :(得分:0)

你需要在python中将String转换为Bytes,而

  

sock.sendall

参考Python: convert string to byte array

根据你的python语法,它应该是python 2。

Python 3可以很容易地找到并坚持在sendall时转换为字节。

在python 2中,您可以使用像

这样的bytearray
  

sock.sendall(bytearray(str(a_converted)))

在python 3中,你可以在转换为字节时调用默认为UTF-8的encode。

  

sock.sendall(str(a_converted).encode())

答案 2 :(得分:0)

实现这一目标最直接的方法是使用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

说明:!字符指定要在编码中使用的字节顺序(此处为big-endian /&#34; network&#34; order),使用I字符这里是数组长度,然后对每个要编码的整数再次执行一次。然后打包实际的数组长度和每个整数。

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

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

使用上面的sendallsock.sendall(encode_int_array( [random.randint(1,255), random.randint(1,255), random.randint(1,255)])) 传输生成的缓冲区:

recv

在C ++端,首先读取4个字节(以获取数组长度),将数组长度转换为本机字节排序,然后读取另外的4 *数组长度字节以获得所有整数;然后将每个转换为本机字节顺序。您应该小心,不要假设SOCK_STREAM将收到您想要的所有数据。 #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 语义不保证这一点。因此,您需要确保收到预期的数字。

C ++方面看起来像这样:

'B'

(如果您只想发送单字节整数,请在上面的'I'调用中替换pack uint8_t,并且还需要相应地调整下面的C ++ - uint32_t取代issues = jira.search_issues('project=projectname') for issue in issues: print (issue.key, 'Status: ',issue.fields.status) ,比如说。)