2017-09-25 22 views
0

過去幾天我正在使用套接字(在C中,沒有套接字編程的經驗)。 其實我必須收集樹莓派上的WiFi數據包,做一些處理,並且必須通過套接字(這兩個設備都連接到網絡中)將格式化的信息發送到另一個設備。以C的速度在C中的套接字上接收不同長度的數據包的連續流?

我面臨的挑戰是通過套接字接收數據。

在發送數據時,數據通過發送端的套接字成功發送,但在接收端,有時會收到一些垃圾或以前的數據。

在發送端(客戶端):

int server_socket = socket(AF_INET, SOCK_STREAM, 0); 
//connecting to the server with connect function 
send(server_socket, &datalength, sizeof(datalength),0); //datalength is an integer containing the number of bytes that are going to be sent next 
send(server_socket, actual_data, sizeof(actual_data),0); //actual data is a char array containing the actual character string data 

接收側(服務器端):

int server_socket = socket(AF_INET, SOCK_STREAM, 0); 
//bind the socket to the ip and port with bind function 
//listen to the socket for any clients 
//int client_socket = accept(server_socket, NULL, NULL); 
int bytes; 
recv(client_socket, &bytes, sizeof(bytes),0); 
char* actual_message = malloc(bytes); 
int rec_bytes = recv(client_socket, actual_message, bytes,0); 

*上面的代碼行是不實際代碼行,但流量和程序是類似的(具有異常處理和評論)。

有時,我可以快速獲取所有數據包的實際數據(沒有任何錯誤和數據包丟失)。但有時候,字節(整數發送來說明下一個事務的字節流的大小)是作爲一個垃圾值接收的,所以我的代碼在那個時候被破壞了。 有時,我在接收端收到的字節數少於預期的字節數(從已收到的整數bytes中得知)。所以在這種情況下,我檢查這種情況並檢索剩餘的字節。

實際上數據包到達的速率非常高(大約在1000個數據包在不到一秒鐘,我不得不解剖,格式化並通過套接字發送它)。我嘗試了不同的想法(使用SOCK_DGRAMS,但這裏有一些數據包丟失,在事務之間插入一些延遲,爲每個數據包打開並關閉一個新套接字,在接收數據包後添加一個確認),但沒有一個符合我的要求(快速傳輸丟包數爲0的數據包)。

請注意,建議一種通過套接字快速發送和接收不同長度的數據包的方法。

+2

什麼是'actual_data'?它是*所有*充滿數據?有沒有部分填充的緩衝區?因爲,你知道,TCP是一個*流*協議,沒有固定大小的數據包或消息邊界。這意味着你可能並不總是通過單一的「recv」調用發送所有這些內容,你必須循環以確保你能收到所有的數據。 –

+1

此外,由於您正在發送數據長度的「int」值(我假設),您確定沒有任何[* endianness *](https://en.wikipedia.org/wiki/Endianness)問題是什麼?由於在典型的ARM平臺(如R-PI)和典型的x86 PC平臺(如大多數現代臺式計算機)上的排序通常不相同。 –

+0

@Someprogrammerdude'實際數據'就像一個英文文本(WiFi數據包解析和格式化) 是的......我知道它可能不會收到一次電話,因爲我添加了一張支票,如果沒有收到一個電話,我已經添加了代碼來獲取剩餘的數據字節。 實際的問題是與字節的數量,它有時收到一些垃圾值 –

回答

1

我看到的幾個主要問題:

  1. 我覺得你的代碼忽略了send功能全緩衝的可能性。

    在我看來,您的代碼忽略了由 recv收集部分數據的可能性。 (沒關係,我只是在電視上看到的新評論)

    換句話說,你需要管理用戶的土地緩衝sendrecv處理碎片。

  2. 該代碼使用sizeof(int),這可能在不同的機器上有不同的長度(也許使用uint32_t代替?)。

  3. 該代碼不會翻譯成網絡字節順序和網絡字節順序。這意味着你發送的是int的內存結構,而不是一個可以被不同機器讀取的整數(一些機器向後存儲字節,一些向前存儲,一些混合和匹配)。

請注意,當您使用TCP/IP發送較大的數據時,它將被分割成更小的數據包。

這取決於MTU的網絡值(通常在野外〜500字節左右,通常在家庭網絡中約1500字節左右)。

爲了處理這些情況,你應該使用一個平坦的網絡設計,而不是阻塞套接字。

考慮通過類似這樣的東西路由send(如果你打算使用阻塞套接字):

int send_complete(int fd, void * data, size_t len) { 
    size_t act = 0; 
    while(act < len) { 
     int tmp = send(fd, (void *)((uintptr_t)data + act), len - act); 
     if(tmp <= 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) 
      return tmp; // connection error 
     act += tmp; 
     // add `select` to poll the socket 
    } 
    return (int)act; 
} 

對於sizeof問題,我會與特定的字節長度整數類型更換intint32_t

的詳細原因

請注意,發送整數分別不確實,這將是單獨接收或整數本身不會被分割擔保。

send功能寫入到系統的緩衝區插座,到網絡上(就像recv從可用的緩衝區中讀取數據,而不是從線)。

無法控制發生碎片的位置或TCP數據包如何打包(除非您實現自己的TCP/IP堆棧)。

我確定你很清楚「垃圾」值是服務器發送的數據。這意味着代碼不會讀取您發送的整數,而是讀取另一條數據。

這可能是與消息邊界對齊的問題,由不完整的read或不完整的send引起。

P.S.

我會考慮在TCP/IP層上使用Websocket協議。

這保證了一個二進制數據包頭,可以與不同的CPU架構(字節順序)一起工作,並提供更多種類的客戶端連接(例如連接瀏覽器等)。它也將解決你遇到的數據包對齊問題(不是因爲它不存在,而是因爲它解決了你將採用的任何Websocket解析器)。

+0

正如我上面提到的,這不是完整的代碼。 但我可以通過檢查以前收到的預期數據和實際接收到的數據字節來獲取數據包的完整發送數據。 但是有時候,預期的數據長度(作爲**單獨的整數**發送)被作爲垃圾值接收到 –

+0

@infiniteloop我編輯了我的答案,添加了一些代碼審查細節,並在最後添加了一個簡短的解釋關於評論。 – Myst

相關問題