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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将数据从套接字从C ++服务器发送到Python客户端时出现问题相关的知识,希望对你有一定的参考价值。

我创建了一个简单的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
", 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
", 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() function以确保写出正确的字节顺序:

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

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

以上是关于将数据从套接字从C ++服务器发送到Python客户端时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

将 UDP 数据从 Python 发送到 Javascript?

如何将数据从 Python 异步套接字服务器发送到子进程?

将数据从服务器发送到客户端套接字编程

从客户端C发送套接字,服务器Python问题

Python 3套接字客户端发送数据和C++套接字服务器接收偏移数据?

将协议缓冲区编码的消息从 Python 服务器发送到 Java 客户端