Posted on April 22nd, 2008 at 12:25 by fr3@K
昨天在網上閒逛, 在樂多上看到篇名為 “Socket Programming in C 常犯的錯誤” 的短文. 這短文作者的動機很好, 指出常見到的錯誤並提供個人看法與可能較好的作法. 這樣的行為很值得鼓勵. 但極可惜的是, 結果卻是很不理想的. 容我說句重話, 如果是考試的話該文是得不到我的分數的.
本想直接在該文回應作者, 但考慮到文中的兩個問題其實是很多不夠老練的 programmer 容易犯的毛病, 因此決定把我的評論寫在這裡, 自己留個紀錄, 也希望能供更多人參考.
原文照貼:
在一個簡單的網路程式,似乎有滿多人都會這樣寫:
char tmp[32];
memset( tmp, ‘\0′, sizeof(tmp));
int ret = recv( sock, tmp, sizeof(tmp), 0 );把 buffer 填滿固然是一件好事,但是字串填滿之後,結果沒有\0了
就會發生意想不到的事情,可能會出現程式記憶體區段錯誤的問題
( 字元陣列的結尾正常結束都要有個 \0 才是正確的 )假設我們不看 ret 回傳收到多大的資料,考慮使用 strcat 來把回傳的 buffer
串到更大的 buffer 字元陣列,應該要扣掉1來使用,避免發生記憶體區段錯誤!char tmp[32];
memset( tmp, ‘\0′, sizeof(tmp));
int ret = recv( sock, tmp, sizeof(tmp) – 1, 0 );
首先是作者在文中指出需要在 buffer 最後保留一個 0x00, 以利後續字串操作. 這原則上是對的, 只是文中舉的例子是 recv, 以 recv/send/read/write 等 API 作 I/O 的時候讀寫的內容是 binary data 而不是 null-terminated string.1 以 socket I/O 來說, 除非 protocol 是如 HTTP 等的文本協議, 否則讀上來的有效 data 很可能中間就有值為 0x00 的 byte.2 而將最後有個 0x00 的 binary data 拿來當作 null-terminated string 操作, 後果可是很難設想的. 雖不至於造成記憶體錯誤, 功能上卻不是正確的. 又由於這樣的 bug 不總是容易重現, 造成的傷害反而可能更大. 所有讀上來的 data 應當要搭配上 recv 的非錯誤 return value (也就是讀出的 byte 數) 才能加以操作, 而不能將它視為字串, 拿來餵給 strlen/strcat 等 API .
另外作者說到, 在使用前先以 0x00 填滿 (fill) 整個 buffer 是好事. 但其實這是沒有任何意義的. 若 recv 失敗, buffer 指向的 memory 根本不會被拿來參考. 要是 recv 成功了, buffer 會被參考的部份則已經被覆蓋掉了. 如果 protocol 是文本協議, 只需如作者所說的在 recv 時先保留一個 byte, 並在成功時在最後補上個 null 就足夠了:
char tmp[32];
int ret = recv( sock, tmp, sizeof(tmp) - 1, 0 );
//if(ret != -1)
//{
// tmp[ret] = 0;
//}
if(ret > 0)
{
tmp[ret] = 0;
// tmp is ready to be manipulated as string
}
else
{
// handle error/EOF here
}
![]() |
|
| Previous Post « Video made for Microsoft’s sales team « |
Next Post » Strong Guarantee using Transaction » |








基本上一般來講只有 return value > 0 才有必要被觀看
然後依照 ret 的數字大小直接對 buffer[ret] = 0×0; 就夠了
會寫這篇文章的主因是針對一般簡單的範例會把收到的資料透過 printf 出來
而在這個時候 %s 如果沒有抓到 就會引起很大的錯誤
for 初學者而言, 我覺得這個細節上探討是沒有必要的
Comment by lilohuang — April 22, 2008 @ 17:56