代码之家  ›  专栏  ›  技术社区  ›  Mike

如何通过UNIX套接字发送std::vector<std::string>?

  •  4
  • Mike  · 技术社区  · 16 年前

    std::vector<std::string> 通过UNIX套接字(本地),并在套接字的另一端获取向量的副本。最简单的方法是什么 O(1) 相对于向量大小的消息(即不为向量中的每个字符串发送消息)?

    因为这都在同一个主机上,而且我控制套接字的两端,所以我不关心特定于机器的问题,例如endinness或向量/字符串表示。

    出于各种原因,我希望避免使用任何外部库。

    5 回复  |  直到 16 年前
        1
  •  12
  •   David Smith    16 年前

    ...
    long length = htonl( vec.size() );
    write( socket, &length, sizeof(length) );
    for ( int i = 0; i < vec.size(); ++i ) {
        length = htonl( vec[i].length() );
        write( socket, &length, sizeof(length) );
        write( socket, vec[i].data(), vec[i].length() );
    }
    ...
    

    拆包方式类似:

    ...
    std::vector vectorRead;
    long size = 0;
    read( socket, &size, sizeof( size ) );
    size = ntohl( size );
    for ( int i = 0; i < size; ++i ) {
        std::string stringRead;
        long length = 0;
        read( socket, &length, sizeof( length ) );
        length = ntohl( length );
        while ( 0 < length ) {
            char buffer[1024];
            int cread;
            cread = read( socket, buffer, min( sizeof( buffer ), length ) );
            stringRead.append( buffer, cread );
            length -= cread;
        }
        vectorRead.push_back( stringRead );
    }
    ...
    
        2
  •  3
  •   Eli Bendersky    16 年前

    为传输和接收打包数据结构通常称为 .

    您可以使用的一个选项是 Boost serialization library 具有序列化STL向量的能力。

        3
  •  1
  •   Nikolai Fetissov    16 年前

    我确信我会被C++狂热分子所大声喊叫,但是 writev(2) (又名。 scatter/gather I/O ). 无论如何,您必须处理接收端的零分隔符。

        4
  •  1
  •   Mike    16 年前

    <string1>\0<string2>\0...<stringN>\0 (预先发送上述字符串的长度)。虽然大卫正确地指出,这将不适用于 std::string 包含空值,我可以保证我的应用程序不会出现这种情况。

        5
  •  0
  •   Lance Diduck    16 年前

    即使在同一台机器上(或者在同一个进程中),也无法通过套接字发送向量。 这有两个问题:

    1. 向量和字符串的dtor将要删除该指针。套接字操作将对您的对象(包括原始指针的值)执行memcpy操作,您将得到双重删除。

    所以规则是这样的:为了通过套接字发送一个对象,它必须是memcpy'd。有几种方法可以做到这一点

    1. 序列化向量像ICE这样的东西很擅长生成这些序列化 http://www.zeroc.com/ 这些都有明显的开销
    2. 创建看起来像vector的只读版本发送端可以是常规的vector接收端可以将recv buffer重新解释为只读实现

    数字3适用于实际存在的所有用例,因为reader很少修改recv缓冲区的内容。如果读者不需要随机访问迭代器,并且可以使用ForwardIterators,那么序列化就相当简单:分配一个缓冲区来保存所有字符串,每个字符串加上一个整数表示长度,再加上一个int表示向量的大小。

    结果可以重新解释为用户定义的结构,该结构是只读字符串的只读集合。因此,不需要太多麻烦,至少可以在读取端得到O(1)。

    要在发送端得到O(1),你必须使用方法2。我已经这样做了,因为我知道我的应用程序永远不会使用超过X长度的字符串,并且向量永远不会包含超过Y个项目。诀窍是,修复容量我永远不必去堆内存。缺点是您发送的是每个字符串的全部容量,而不仅仅是所使用的内容。然而,在许多情况下,发送所有内容要比压缩快得多,尤其是在同一台机器上——在这种情况下,您可以将此结构放入共享内存中,并通知recv应用程序查找它。

    您可能想看看boost进程间处理,以获得更多关于如何使容器在没有序列化的情况下通过套接字的想法。

    推荐文章