簡體   English   中英

通過套接字從C ++服務器向Python客戶端發送數據時出現問題

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

我創建了一個簡單的C ++服務器,試圖通過套接字將vector<uint8_t>的字節發送到Python客戶端。 我的所有服務器和客戶端連接正常,但是數據在python中錯誤地輸出。 我首先發送一個整數,描述期望的字節數,然后發送字節。 但是,當我運行python腳本並在套接字上調用recv時,有時我得到的預期字節數值不正確。

我已經調試了C ++和Python代碼,並且顯示出一些奇怪的行為。 首先,我意識到在調試Python代碼時,它運行良好。 事實證明,在我的Python腳本中,如果我在讀取整數標頭的前4個字節與讀取其余字節之間的時間之間調用sleep(1) ,那么它將起作用。 但是,當我在不睡覺的情況下進行多個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,)

網絡數據可能會緩慢到達,而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連接上的網絡順序是big-endian,因此您的C ++代碼需要使用該順序,並使用int.from_bytes(..., byteorder="big") 而不使用socket.ntohl() byteorder參數與平台無關,使用此方法將字節解釋為整數,不受平台差異的影響。

現在,您的C ++代碼根本不處理字節順序;它只處理字節順序。 在服務器代碼中使用htonl()函數以確保您寫出正確的字節順序:

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

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

暫無
暫無

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

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