2017-02-18 193 views
0

我想通過udp實現距離矢量路由。我有以下結構:recvfrom用零填充緩衝區

struct cost_info { 
    uint8_t id; 
    uint8_t pad; 
    uint16_t cost; 
}__attribute__((__packed__)); 

struct advertisement_packet { 
    uint8_t type; 
    uint8_t version; 
    uint16_t num_updates; 
    struct cost_info data[]; 
}__attribute__((__packed__)); 

所以數據包是1點字節的類型,1級字節的版本,2個字節更新計數和數據,那麼4 *更新計數字節。我使用靈活的陣列成員,以便我可以做

sendto(fd, pkt, sizeof(struct advertisement_packet) + pkt->num_updates * sizeof(struct cost_info), 0, addr, sizeof(struct sockaddr_in)); 

並將整個事件發送到一個數據包中。發送一個數據包是必需的。 我能夠使用wireshark驗證數據是否正確發送。在接收端,我做了兩次接收。第一個獲得前4個字節,所以我可以知道有多少更新期望。然後我相應地調整我的接收緩衝區大小,並接收4 * num_updates個字節。問題是這第二次接收填充我的緩衝區零!如果我期望12個字節的數據,我會得到12個字節的零。下一次從該套接字讀取時,我會按預期得到下一個數據包的開頭。爲什麼會發生?

這是我的接收代碼,如果需要的話。

recvfrom(i, pkt, sizeof(struct advertisement_packet),0,NULL,0); 
//reallocate more space if the incoming data will need it 
if (pkt->num_updates > pkt_cost_slots) { 
    pkt_cost_slots *= 2; 
    pkt = realloc(pkt,sizeof(struct advertisement_packet) + sizeof(struct cost_info) * pkt_cost_slots); 
    assert(pkt); 
} 
//receive data 
recvfrom(i,pkt->data,pkt->num_updates * sizeof(struct cost_info),0,NULL,0); 

回答

1

不能超過兩recv()/recvfrom()電話分裂收到一個數據報。面向數據報的套接字的基本性質是發送和接收數據報的單元。但是,您可以閱讀部分或全部數據報,而不必將其從接收隊列中刪除(即「偷看」)。 The specifications for recvfrom()這樣說:

對於基於消息的插座,如SOCK_RAWSOCK_DGRAM,和SOCK_SEQPACKET,整個消息應被 在單個操作中讀取。如果消息太長而無法適應 提供的緩衝區,並且MSG_PEEK沒有在flags參數中設置,則會丟棄超出的字節 。

您可以通過在第一recvfrom()呼叫使用MSG_PEEK從而實現自己的目標,但要注意,那麼你就需要接收整個數據報在第二recvfrom()呼叫,而不只是一部分,你沒有讀第一次。

+0

試過這個。我能夠獲得大小,然後第二次讀取全部金額。不幸的是,儘管第二次讀取表明它已經獲得了全部16個字節,但數據被清零。 – Sandles

+0

@Sandles,如果您可以在接收方驗證,例如通過wireshark,傳入的數據是按照您的期望結構化的,那麼我最好的猜測是接收者對發佈者的'struct advertisement_packet'和/或'struct cost_info'的佈局有不同的想法。這可能會使*看起來*收到的數據全部爲零。我建議通過將'pkt'強制轉換爲'unsigned char *'來檢查實際收到的數據,並逐字節地檢查。 –

+0

Welp。我鑄造它,它肯定是獲取數據。我仍然需要弄清楚爲什麼我的結構訪問返回0,但我得到的數據,這是重要的。謝謝!! – Sandles

1

UDP是數據報協議。整個數據報包被傳遞給第一個recvfrom調用。如果您的緩衝區大小不足,則數據字節將丟失。他們不會傳遞給後續的recvfrom

所以解決的辦法是隻傳入一個足夠大的緩衝區,用於任何UDP數據報,然後將其轉換爲您的數據類型。

unsigned char buffer[65535]; 
struct advertisement_packet *pkt = (advertisement_packet *)buffer; 
recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen); 

這裏有一個更好的解決方案,也做的常規安全檢查,所以你不要上當受騙成惡意攻擊者的緩衝區溢出。

int result = 0; 
unsigned char buffer[65535]; 
struct advertisement_packet *pkt = (advertisement_packet *)buffer; 
sockaddr_in addr; 
socklen_t addrlen = sizeof(addr); 
int accepted = false; 

result = recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen); 

if (result > 0) 
{ 
    size_t expected_size = sizeof(struct advertisement_packet) + pkt->num_updates * sizeof(struct cost_info); 

    if (result >= expected_size) 
    { 
     accepted = true; 
    } 
    else 
    { 
     // packet is malformed or undersized, so let's avoid a buffer overrun 
     result = -1; 
    } 
} 

if (accepted) 
{ 
    // got packet and it's valid 
    // continue parsing it, or make a copy of it. 
} 
+0

只要不需要重新分配內存,這是一個好主意。不過,我只是試了一下,並沒有解決這個問題。它爲我的測試收到了預期的16個字節,並且第一個字節仍然正確,其餘數據仍然被清零。 – Sandles

相關問題