Проблема отправки данных через сокет с сервера C ++ на клиент Python - PullRequest
0 голосов
/ 29 апреля 2019

Я создал простой сервер C ++, который пытается отправить байты vector<uint8_t> клиенту Python через сокет.У меня есть все сервер и клиент, соединяющийся нормально, однако данные неправильно выводятся в python.Сначала я посылаю целое число, описывающее, сколько байтов ожидать, а затем отправляю байты.Однако, когда я запускаю свой скрипт на Python и вызываю recv в сокете, я иногда получаю неправильные значения ожидаемого количества байтов.

Я отлаживал C ++ и код Python, и этопоказывая какое-то странное поведение.Сначала я понял, что когда я отлаживал код Python, он работал нормально.Оказывается, что в моем скрипте Python, если я вызываю sleep(1) между тем, когда я читаю первые 4 байта для целочисленного заголовка, и когда я затем читаю остальные байты, это работает.Однако, когда я делаю множественные recv звонки без сна, то все идет не так.Я также проверил, что байтовый порядок байтов правильный.Я даже добавил процедуру рукопожатия между сервером и клиентом, чтобы убедиться, что они хорошо работают вместе.

Для 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;
}

Отправка данных и методы рукопожатия:

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:

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 ++ и ожидаемые размеры:

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:

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,)

1 Ответ

4 голосов
/ 29 апреля 2019

Сетевые данные могут поступать медленно, и socket.recv() даст вам до запрошенного количества байтов, но меньше, если в буфере еще недостаточно данных.

Вам нужно продолжать звонить recv(), пока не будут получены все байты. Это достаточно часто, так что вы хотите, чтобы функция обрабатывала повторные вызовы для вас:

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

и используйте эту функцию для получения ваших данных:

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

Примечание о порядке следования байтов: от сервера зависит отправка данных в определенном порядке следования байтов. Сетевой порядок в соединениях TCP / IP имеет порядок байтов, поэтому ваш код C ++ должен использовать этот порядок и использовать int.from_bytes(..., byteorder="big") без с использованием socket.ntohl(). Аргумент byteorder не зависит от платформы, интерпретация байтов с помощью этого метода как целых не зависит от платформы.

В данный момент ваш код C ++ вообще не обрабатывает порядок байтов; используйте функцию htonl() в коде вашего сервера, чтобы убедиться, что вы записали правильный порядок байтов:

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

и затем используйте int.from_bytes(..., byteorder="big") в Python.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...