深入理解Socket的讀寫

前言

對於Linux網絡編程,有很多坑需要我們去踩。在這個時候,我們纔會知道理論知識的重要性。無論是哪種語言,網絡編程都可以寫成厚厚的一本書。舉個例子,比如“當網絡斷掉,我們調用write去往socket中寫入數據,爲什麼返回正常寫入呢?”。所以有空多看看《TCP/IP詳解》,《UNIX網絡編程》等經典書籍來補充網絡知識。

深入理解write

首先,我們來解決上面的問題。爲什麼網絡斷了,還能write還是返回成功呢?我們先看write的定義:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

對於write的返回值,它表示寫入的字節數。而這個寫入,是寫到哪呢?是成功發送的意思嗎? 其實並不是,write成功返回,只是buf中的數據被複制到了kernel中的TCP發送緩衝區。至於數據什麼時候被髮往網絡,系統調用層面不會給予任何保證和通知。

所以,我們並不能直接根據write的返回值來判定發送成功,這也就是爲什麼網絡斷掉了,write的返回值和我們希望寫入的值是一樣的。

阻塞和非阻塞

讀寫操作肯定會涉及阻塞和非阻塞的問題。那write和read在什麼情況下會阻塞呢?當kernel中該socket的發送緩衝區已滿時write會阻塞。而read調用阻塞,通常是發送端的數據沒有到達。

對於每個socket,擁有自己的發送緩衝區和接收緩衝區。兩個緩衝區大小都由系統來自動調節,但一般在default和max之間浮動。
在這裏插入圖片描述
默認socket是阻塞的,將一個socket 設置成非阻塞模式,可以使用fcntl方法:

int flags;
if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
    return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
    return -1;
}

阻塞(默認)和非阻塞模式下read/write行爲的區別:

  1. read總是在接收緩衝區有數據時立即返回,而不是等到給定的read buffer填滿時返回。只有當接收緩衝區爲空時,阻塞模式纔會等待,而非阻塞模式下會立即返回-1。

  2. 阻塞的write只有在緩衝區足以放下整個buffer時才返回。非阻塞write則是返回能夠放下的字節數,之後調用則返回-1。

對於阻塞的write有個特殊情況:

當write阻塞等待時,對端關閉了socket,則write則會立即將剩餘緩衝區填滿並返回所寫的字節數,再次調用write則會失敗。

更多精彩文章,歡迎關注"嵌入式軟件開發交流"

歡迎大家關注我的微信公衆號!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章