2012-12-01 357 views
3

我有一個TCP服務器和客戶端,並帶有已建立的套接字。比方說,我有以下情況:分別通過TCP套接字發送和接收字符串

SERVER:

char *fn = "John"; 
char *ln = "Doe"; 

char buffer[512]; 

strcpy(buffer, fn); 
send(*socket, buffer, strlen(buffer), 0); 
strcpy(buffer, ln); 
send(*socket, buffer, strlen(buffer), 0); 

客戶:

char *fn; 
char *ln; 

char buffer[512]; 

bytes = recv(*socket, buffer, sizeof(buffer), 0); 
buffer[bytes] = '\0'; 
strcpy(fn, buffer); 

bytes = recv(*socket, buffer, sizeof(buffer), 0); 
buffer[bytes] = '\0'; 
strcpy(ln, buffer); 

printf("%s", fn); 
printf("%s", ln); 

預期結果: 我想單獨接收每個字符串(如我在不同的變量保存它們),而是第一個recv()將fn「John」和ln「Doe」連接成「JohnDoe」,並且程序被卡住在第二個recv()上,因爲我已經完成了發送。

我試過如下: - 添加\ 0到FN和LN的字符串,像這樣:

char *fn = "John\0"; 
char *ln = "Doe\0"; 

但問題依然存在。

是否有某種我可以施加的條件,以便每個字符串都是分開接收的?

編輯補充更多的問題:

1)它爲什麼會在某些情況下,實際上分別給他們,和其他人將它們發送聯合?

2)我應該發送()和recv()或者?在我的代碼的其他部分,似乎它是自我調節的,只在正確的時間接收正確的字符串,而不必檢查。這是否有訣竅?

3)關於發送一個包含我將要發送的字符串長度的頭部,我如何解析傳入的字符串以知道在哪裏分割?任何例子都會很棒。

4)我一定會檢查一下,如果我得到的東西比我的緩衝區大,謝謝指出。

編輯2:

5)因爲我總是把事情大小的緩衝區字符數組512不應該每次發送操作把緩衝區的全尺寸即使我發送1個字母串?這是令人困惑的。我有「約翰」,但我把它放在一個大小爲512的數組中,發送它,不應該填滿發送緩衝區,所以recv()函數會清空網絡緩衝區,同時拉動整個數組的大小512?

+0

你跳過寫一個客戶機/服務器應用程序的非常拳頭一步 - 設計客戶端和服務器用於交換信息的協議。如果你從來沒有設計過一個協議,那麼你就不能實現一個協議,然後你的代碼只能運行,如果它工作的話,那麼運氣好。顯然你想發送消息,但TCP不是消息協議。那麼你使用的是什麼消息協議? –

回答

8

首先,當客戶端調用recv(*socket, buffer, sizeof(buffer), 0);,它會從網絡接收高達的sizeof(緩衝區)字符(在這裏,512)。這是來自網絡緩衝區,與在服務器上調用了多少次send無關。 recv不尋找\0或任何其他字符 - 它是從網絡緩衝區的二進制拉。它將讀取整個網絡緩衝區或sizeof(buffer)個字符。

您需要檢查第一個recv以查看它是否同時具有姓氏和名字,並對其進行適當處理。

您可以通過讓服務器將字符串的長度作爲數據流的頭幾個字節(「標題」)發送,或者通過掃描接收到的數據來查看是否有兩個字符串。

無論何時調用recv,您都需要執行檢查 - 如果字符串長度超過512字節,該怎麼辦?那麼你需要多次撥打recv才能得到字符串。而如果你得到第一個字符串,只有第二個字符串的一半呢?通常這兩種情況只會在處理大量數據時纔會發生,但我以前在我自己的代碼中看到過這種情況,所以最好在recv中包含好的檢查,即使在處理這樣的小字符串時也是如此。如果你儘早練習良好的編碼策略,你就不會有太多令人頭疼的事情。

+1

另外,如果字符串足夠長,甚至有可能單個讀取不會返回整個字符串。接收邏輯必須考慮到這一點, – mac

+0

好點,@mac,我已經更新了我的答案。 – cegfault

2

正確的做法是將發送的數據與標題放在一起。所以你可以傳遞消息的大小,包括消息的大小,然後在另一邊你可以計算消息的長度。

約翰是4個字符,所以你有長度,就像「0004約翰」,那麼你會知道什麼時候停止閱讀。您可能還想在標題中添加一條命令,這很常見。例如,名字可能是1,第二個名字是2;

例如「01004JOHN」將被設置在緩衝區中並被髮送。

爲此,您將項目添加到緩衝區,然後按您添加的值的大小增加緩衝區中的插入點。您可以使用它將許多部分添加到緩衝區。 *確保你不會超出緩衝區的大小。

記住這些示例im給予不正確,因爲您不傳遞數字的字符串表示形式,只是數字,所以它將是一個long int 1,long int 4,然後是chars或unicode,無論哪種方式, 4個字符。

+0

如果你有一個ASCII協議,字符串表示非常好。 – glglgl

+0

如何在發送字符串之前發送一個long int,send()只接受一個(void *)作爲第三個參數。另外,我不明白的是sizeof()應該已經在照顧這個尺寸明智的否?我發送一個8字節的字符串,不應recv()讀取8個字節並停止? – user1202888

+0

你不發送它之前,你發送緩衝區,你只需填充緩衝區,所以你可以使用指針算術移動緩衝區插入點。這裏是一個示例問題的想法http://stackoverflow.com/questions/11680309/c-buffer-combining-adding-extra-empty-values你只需添加值,然後提前您剛剛添加到項目的大小緩衝區 – nycynik

2

TCP是面向流的,而不是面向數據報的。如果是後者,你的東西會很好。您有以下選擇:

  • 使用SCTP
  • 使用UDP,如果你能承受失去可靠性
  • 的方式,你可以工作,要麼在前面加上一個長度標記左右或通過添加字符串分開,一個用於終止每個字符串的NUL字節。

對於後者,你只是做send(*socket, buffer, strlen(buffer)+1, 0);爲包括NUL是這裏然而,並且發件人會反覆recv(),找到一個字符串結束,直到它找到2串。

請注意,您應該注意strcpy():只有在確定您寫入的字符串正確的情況下才能使用它。

你的接收器確實

bytes = recv(*socket, buffer, sizeof(buffer), 0); 
buffer[bytes] = '\0'; 

這是不好的,因爲,如果緩衝區已滿,所以bytes == sizeof(buffer)(可在更復雜的情況發生),buffer[bytes] = '\0'寫入超出緩衝區。所以使用

bytes = recv(*socket, buffer, sizeof(buffer) - 1, 0); 

,你可以愉快地做

buffer[bytes] = '\0'; 

0

代替的sizeof(緩衝液)使用strlen(FN);所以它只會傳輸確切數量的字節..如果您需要空終止符一起使用strlen(fn)+1,但不要忘記連接空終止符與strcat()

如果您發送所有緩衝區大小沒有與memset的清潔你也有垃圾一起,所以要小心的..

0

對於socket流是使用某種消息幀(也稱爲長度前綴取景)一個很好的做法。

或者使用一個庫,像nanomsgzeromq

libuv是基於流的,但我們可以用libuv_message_framing它。

或協議一樣SCTPWebsocket

您也可以發送前串行數據。這可以用Binnnetstrings來完成:

4:John,3:Doe, 
相關問題