简体   繁体   English

通过套接字从C ++服务器向Python客户端发送数据时出现问题

[英]Problem sending data over socket from C++ server to Python client

I have created a simple C++ server that is attempting to send the bytes of a vector<uint8_t> to a Python client over a socket. 我创建了一个简单的C ++服务器,试图通过套接字将vector<uint8_t>的字节发送到Python客户端。 I have got all the server and client connecting fine, however the data is coming out incorrectly in python. 我的所有服务器和客户端连接正常,但是数据在python中错误地输出。 I am first sending an integer describing how many bytes to expect, then sending the bytes. 我首先发送一个整数,描述期望的字节数,然后发送字节。 However, when I run my python script and call recv on the socket, I am sometimes getting incorrect values for the amount of bytes to expect. 但是,当我运行python脚本并在套接字上调用recv时,有时我得到的预期字节数值不正确。

I have debugged the C++ and the Python code, and it is showing some weird behaviour. 我已经调试了C ++和Python代码,并且显示出一些奇怪的行为。 Firstly I realised that when I was debugging the Python code it was working fine. 首先,我意识到在调试Python代码时,它运行良好。 It turns out that in my Python script, if I call sleep(1) between when I read the first 4 bytes for the integer header and when I then read the rest of the bytes it works. 事实证明,在我的Python脚本中,如果我在读取整数标头的前4个字节与读取其余字节之间的时间之间调用sleep(1) ,那么它将起作用。 However, when I do the multiple recv calls without the sleep, then things go wrong. 但是,当我在不睡觉的情况下进行多个recv呼叫时,事情就会出错。 I have also checked that the byte endian stuff is correct. 我还检查了字节尾数是否正确。 I even added a handshake procedure between the server and client to make sure they were working together nicely. 我什至在服务器和客户端之间添加了一个握手过程,以确保它们之间的协作良好。

For the C++: Setting up the socket: 对于C ++:设置套接字:

int Server::setup_socket(int port){
    int server_fd, new_socket; 
    struct sockaddr_in address; 
    int opt = 1; 
    int addrlen = sizeof(address); 

    // Creating socket file descriptor 
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0){ 
        perror("socket failed"); 
        exit(EXIT_FAILURE); 
    } 

    // Forcefully attaching socket to the port 
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))){ 
        perror("setsockopt"); 
        exit(EXIT_FAILURE); 
    } 
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = INADDR_ANY; 
    address.sin_port = htons(port); 

    // Forcefully attaching socket to the port 
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0){ 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 
    if (listen(server_fd, 3) < 0){ 
        perror("listen"); 
        exit(EXIT_FAILURE); 
    } 
    printf("Successfully connected to port %d\n", port);
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0){ 
        perror("accept"); 
        exit(EXIT_FAILURE); 
    } 

    return new_socket;
}

Sending data and handshake methods: 发送数据和握手方法:

void Server::send_image(cv::Mat &image) {
    std::vector<uint8_t> buf;
    std::vector<int> param(2);
    param[0] = cv::IMWRITE_JPEG_QUALITY;
    param[1] = 80; //default(95) 0-100
    cv::imencode(".jpg", image, buf, param);

    int length = buf.size();
    printf("Sending image of size: %d\n", length);
    write(data_socket, &length, sizeof(length));
    write(data_socket, buf.data(), length);
}

void Server::confirm_sent(){
    uint8_t confirmation[1];
    write(conf_socket, confirmation, 1);
}

void Server::confirm_received(){
    uint8_t confirmation[1];
    read(conf_socket, confirmation, 1);
}

Python code: Python代码:

data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # For sending data
conf_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # For hand shaking

# Connect the socket to the port where the server is listening
data_address = ('192.168.1.146', 2323)
conf_address = ('192.168.1.146', 2324)
print('connecting to %s port %s' % data_address)
data_sock.connect(data_address)
time.sleep(1)
print('connecting to %s port %s' % conf_address)
conf_sock.connect(conf_address)

while True:
    conf_sock.recv(1)  # Confirm sent
    size1 = int.from_bytes(data_sock.recv(4), byteorder="big") 
    size2 = socket.ntohl(size1) # Fixes endian problems
    # time.sleep(1) # Inserting this fixes things, but I don't want the delay
    data = np.frombuffer(data_sock.recv(size2), dtype=np.uint8)
    print(f"{size1}, {size2}, {data.shape}")
    conf_sock.send(bytes(1))

C++ output and expected sizes: C ++输出和预期大小:

Max speed spi is 8000000
OV5642 detected.
Successfully connected to port 2323
Successfully connected to port 2324
Sending image of size: 134966
Sending image of size: 135072
Sending image of size: 134628
Sending image of size: 134846
Sending image of size: 134704
Sending image of size: 134885
Sending image of size: 133942

Python received sizes: Python收到的大小:

connecting to 192.168.1.146 port 2323
connecting to 192.168.1.146 port 2324
906953216, 134966, (95568,)
1224436735, 4285266760, (45190,)
2585803520, 3874970, (137968,)
939478527, 4283301687, (137524,)
103119361, 24782086, (136294,)
1526714366, 4275044186, (127464,)
469746175, 4290903835, (136333,)

Network data can arrive slowly, and socket.recv() will give you up to the requested number of bytes, but fewer if there isn't enough data in the buffer yet. 网络数据可能会缓慢到达,而socket.recv()将为您提供所请求的字节数,但是如果缓冲区中没有足够的数据,字节数将减少。

You need to keep calling recv() until all bytes have arrived. 您需要继续调用recv()直到所有字节都到达为止。 This is common enough that you'd want a function to handle the repeated calls for you: 这很常见,以至于您希望函数为您处理重复的调用:

def socket_read(sock, expected):
    """Read expected number of bytes from sock

    Will repeatedly call recv until all expected data is received

    """
    buffer = b''
    while len(buffer) < expected:
        buffer += sock.recv(expected - len(buffer))
    return buffer

and use that function to receive your data: 并使用该函数接收您的数据:

message_size = int.from_bytes(socket_read(data_sock, 4), byteorder="little")
data = np.frombuffer(socket_read(data_sock, message_size), dtype=np.uint8)

A note on byte order: it is up to the server to send data in a specific byte order. 关于字节顺序的注释:服务器以特定的字节顺序发送数据。 Network order on TCP/IP connections is big-endian, so your C++ code needs to use that ordering, and use int.from_bytes(..., byteorder="big") without using socket.ntohl() . TCP / IP连接上的网络顺序是big-endian,因此您的C ++代码需要使用该顺序,并使用int.from_bytes(..., byteorder="big") 而不使用socket.ntohl() The byteorder argument is platform agnostic, interpreting bytes with this method as integers is not subject to platform differences. byteorder参数与平台无关,使用此方法将字节解释为整数,不受平台差异的影响。

Right now, your C++ code is not handling byte ordering at all; 现在,您的C ++代码根本不处理字节顺序;它只处理字节顺序。 use the htonl() function in your server code to ensure you write out the correct byte order: 在服务器代码中使用htonl()函数以确保您写出正确的字节顺序:

write(data_socket, &(htonl(length)), sizeof(length));

and then use int.from_bytes(..., byteorder="big") in Python. 然后在Python中使用int.from_bytes(..., byteorder="big")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用从Python(客户端)到C ++(服务器)阵列的套接字发送数据 - Sending data using socket from Python(client) to C++(server) arrays Python 3套接字客户端发送数据,而C ++套接字服务器接收带偏移量的数据? - Python 3 socket client sending data and C++ socket server receiving data with offset? 如何将从 C++ 客户端套接字接收的二进制数据转换为 python 服务器套接字 - How can I convert binary data received from C++ client socket to python Server socket 通过蓝牙将数据从Android(客户端)发送到Python(服务器) - Sending data from Android (Client) to Python (Server) over Bluetooth 从python中的服务器发送一些行到QT(C ++)客户端 - Sending some lines from server in python to QT(C++) client 通过套接字连接将图像从客户端发送到服务器 - Sending an image from client to server over a socket connection 将图像从套接字服务器(java)发送到套接字客户端(python) - sending Image from Socket server (java) to socket client (python) Python Socket编程:在服务器和客户端之间发送和接收int数据? - Python Socket Programming: sending and receiving int data between server and client? python 套接字将数据从客户端发送到服务器 - python socket send data from client to server 通过套接字/ ftp python从服务器向客户端发送文件 - Sending files to client from server via socket/ftp python
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM