簡體   English   中英

我怎樣才能發送std :: vector <std::string> 通過UNIX套接字?

[英]How can I send an std::vector<std::string> over a UNIX socket?

對於我的應用程序,我需要能夠通過UNIX套接字(本地)發送std::vector<std::string> ,並在套接字的另一端獲取向量的副本。 使用相對於向量大小的O(1)消息(即不向向量中的每個字符串發送消息O(1) ,最簡單的方法是什么?

因為這都在同一個主機上,並且因為我控制套接字的兩端,所以我不關心機器特定的問題,例如endinness或vector / string表示。

出於各種原因,我想避免使用任何外部庫。

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

用於發送和接收的打包數據結構通常稱為序列化

您可以使用的一個選項: Boost序列化庫具有序列化STL向量的功能。

另一個是自己動手 - 在這種情況下應該不難。 例如,您可以將向量的所有字符串連接成一個單獨的字符串(每個成分NULL分開)並發送該緩沖區,然后以類似方式恢復它。

我敢肯定我會被C ++狂熱分子大吼大叫,但嘗試writev(2) (又稱分散/聚集I / O )。 無論如何,你必須在接收端處理零分隔符。

我最終采用的解決方案是以<string1>\\0<string2>\\0...<stringN>\\0的形式序列化字符串向量(事先發送上述字符串的長度)。 雖然David正確地指出這對於std::string包含null的情況不起作用,但我可以保證這不適用於我的應用程序。

無法通過套接字發送向量,即使是在同一台機器上(或者甚至在同一個進程中也是如此)。 這有兩個問題:

  1. 向量和字符串都維護內存指針到原始內存。 這排除了將vector <,string>發送到另一個進程
  2. 向量和字符串的dtors將要刪除該指針。 套接字操作將對你的對象進行memcpy(包括原始指針的值),你將獲得雙重刪除。

所以規則是這樣的:為了通過套接字發送對象,它必須能夠被memcpy。 做這件事有很多種方法

  1. 序列化向量像ICE這樣的東西很擅長生成這些序列化http://www.zeroc.com/這些都有明顯的開銷
  2. 使用與vector和string相同的接口創建一些東西,但是能夠被memcpy
  3. 創建看起來像向量的只讀版本發送方可以是常規向量,recv端可以將recv緩沖區作為只讀實現重新解釋

2號通常很難做到,但有一些限制是可能的。 對於高性能應用程序,在任何情況下都不會使用向量。

數字3適用於所有用例,因為讀者很少修改recv緩沖區的內容。 如果讀者不需要隨機訪問迭代器,並且可以使用ForwardIterators,則序列化非常簡單:分配一個可以容納所有字符串的緩沖區,加上每個表示長度的整數加上一個用於向量大小的int。

結果可以重新解釋為用戶定義的結構,該結構是只讀字符串的只讀集合。 所以沒有太多麻煩你至少可以在閱讀方面獲得O(1)。

要在發送方獲得O(1),你必須使用方法2.我已經完成了這一點,知道我的應用程序永遠不會使用超過X長度的字符串,並且該向量永遠​​不會超過Y項目。 訣竅是修復容量我永遠不會去堆內存。 缺點是你要發送每個字符串的全部容量,而不僅僅是使用了什么。 然而,在許多情況下,發送所有內容的速度遠遠快於嘗試壓縮它,特別是如果你在同一台機器上 - 在這種情況下你可以將這個結構放在共享內存中並通知recv應用程序只是尋找它。

您可能需要查看boost interprocess以獲取有關如何制作可以通過套接字推送而無需序列化的容器的更多想法。

暫無
暫無

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

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