1

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. I have got all the server and client connecting fine, however the data is coming out incorrectly in 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.

I have debugged the C++ and the Python code, and it is showing some weird behaviour. Firstly I realised that when I was debugging the Python code it was working fine. 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. However, when I do the multiple recv calls without the sleep, then things go wrong. 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:

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:

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:

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:

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,)
2
  • Side note: If you have an endian problem, don't decode as big endian, but as little endian. So drop the socket.ntohl() call, and replace 'big' with 'little' on the preceding line. Commented Apr 29, 2019 at 17:19
  • (and network byte order for TCP/IP is always big-endian, if you have an ordering issue then the C++ side is not sending data in the right order, e.g. it is sending data in little-endian order) Commented Apr 29, 2019 at 17:23

1 Answer 1

5

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.

You need to keep calling recv() until all bytes have arrived. 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(). The byteorder argument is platform agnostic, interpreting bytes with this method as integers is not subject to platform differences.

Right now, your C++ code is not handling byte ordering at all; use the htonl() function in your server code to ensure you write out the correct byte order:

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

and then use int.from_bytes(..., byteorder="big") in Python.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.