簡體   English   中英

使用從Python(客戶端)到C ++(服務器)陣列的套接字發送數據

[英]Sending data using socket from Python(client) to C++(server) arrays

我一直在嘗試使用套接字將數組從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();
}

收到的信息是正確的,但我無法正確轉換數據。 我試圖使用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

說明: ! character指定要在編碼中使用的字節順序(此處為big-endian /“network”順序),此處使用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個字節(以獲得數組長度),將數組長度轉換為本機字節排序,然后讀取另外的4 *數組長度字節以獲得所有整數; 然后將每個轉換為本機字節順序。 您應該小心,不要假設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 。)

這聽起來像是序列化問題。 由於您想連接兩種不同的語言,我建議使用標准化的,眾所周知的格式,如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格式
  • 找到一個cSQL JSON庫,它可以將數據反序列化為對象

我已經看到這個在使用中,因為每個數據類型都被視為一個字符串來發送它,並且在反序列化方面,你需要為每種情況確定它實際上是哪種數據類型(將字符串轉換為double或int或float或者布爾等...)

您需要將python中的String轉換為Bytes while

sock.sendall

參考Python:將字符串轉換為字節數組

根據你的python語法,它應該是python 2。

Python 3可以很容易地找到並堅持在sendall時轉換為Bytes。

在python 2中,你可以像使用bytearray一樣

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

在python 3中,您可以在轉換為字節時調用默認為UTF-8的encode。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM