如何在UNIX套接字上发送std :: vector <std :: string>?</std :: string>

时间:2010-04-17 04:26:36

标签: c++ unix sockets

对于我的应用程序,我需要能够通过UNIX套接字(本地)发送std::vector<std::string>,并在套接字的另一端获取向量的副本。使用相对于向量大小的O(1)消息执行此操作的最简单方法是什么(即,不为向​​量中的每个字符串发送消息)?

由于这一切都在同一台主机上,并且因为我控制了套接字的两端,所以我并不关心机器特定的问题,例如endinness或vector / string表示。

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

5 个答案:

答案 0 :(得分:12)

std :: string不会阻止你在你的字符串中使用nuls。只有当您尝试将这些与非敏感API一起使用时才会遇到麻烦。我怀疑你会通过预先增加数组的大小,然后是线上每个字符串的长度来序列化数组。

...
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 );
}
...

答案 1 :(得分:3)

用于传输和接收的打包数据结构通常称为序列化

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

另一个是自己动手 - 在这种情况下应该不难。例如,您可以将向量的所有字符串连接成一个单独的字符串(每个成分分隔NULL)并发送该缓冲区,然后以类似方式恢复它。

答案 2 :(得分:1)

我确信我会被C ++狂热分子大吼大叫,但请尝试writev(2)(a.k.a。scatter/gather I/O)。无论如何,你必须在接收端处理零分隔符。

答案 3 :(得分:1)

我最终采用的解决方案是以<string1>\0<string2>\0...<stringN>\0的形式序列化字符串向量(事先发送上述字符串的长度)。虽然David正确指出这对于std::string包含null的情况不起作用,但我可以保证我的应用程序不会出现这种情况。

答案 4 :(得分:0)

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

  1. vector和string都维护内存指针到原始内存。这排除了发送矢量&lt;,string&gt;到另一个过程
  2. 向量和字符串的dtors将要删除该指针。套接字操作将对你的对象进行memcpy(包括原始指针的值),你将获得双重删除。
  3. 所以规则是这样的:为了通过套接字发送对象,它必须能够被memcpy。有几种方法可以做到这一点

    1. 序列化向量像ICE这样的东西擅长生成这些序列化http://www.zeroc.com/这些具有明显的开销
    2. 使用与矢量和字符串相同的界面创建一些东西,但能够记忆
    3. 创建看起来像向量的只读版本发送方可以是常规向量,recv端可以将recv缓冲区重新解释为直播,作为只读实现
    4. 2号通常很难做到,但有一些限制是可能的。对于高性能应用程序,在任何情况下都不会使用向量。

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

      结果可以重新解释为用户定义的结构,该结构是只读字符串的只读集合。所以没有太多麻烦你至少可以在阅读方面获得O(1)。

      要在发送方获得O(1),你必须使用方法2.我已经完成了这一点,知道我的应用程序永远不会使用超过X长度的字符串,并且该向量将永远不会保留超过Y项。诀窍是修复容量我永远不会去堆内存。缺点是你要发送每个字符串的全部容量,而不仅仅是使用了什么。然而,在许多情况下,只是发送所有东西比尝试压缩它要快得多,尤其是如果你在同一台机器上 - 在这种情况下你可以将这个结构放在共享内存中并通知recv应用程序只是寻找它。 / p>

      您可能需要查看boost进程以获取有关如何制作可以通过套接字推送而无需序列化的容器的更多想法。