① socket阻塞和非阻塞的區別
阻塞socket和非阻塞socket的區別:
1、讀操作
對於阻塞的socket,當socket的接收緩沖區中沒有數據時,read調用會一直阻塞住,直到有數據到來才返回。當socket緩沖區中的數據量小於期望讀取的數據量時,返回實際讀取的位元組數。當sockt的接收緩沖區中的數據大於期望讀取的位元組數時,讀取期望讀取的位元組數,返回實際讀取的長度。
對於非阻塞socket而言,socket的接收緩沖區中有沒有數據,read調用都會立刻返回。接收緩沖區中有數據時,與阻塞socket有數據的情況是一樣的,如果接收緩沖區中沒有數據,則返回錯誤號為EWOULDBLOCK,表示該操作本來應該阻塞的,但是由於本socket為非阻塞的socket,因此立刻返回,遇到這樣的情況,可以在下次接著去嘗試讀取。如果返回值是其它負值,則表明讀取錯誤。
2、寫操作
對於寫操作write,原理是類似的,非阻塞socket在發送緩沖區沒有空間時會直接返回錯誤號EWOULDBLOCK,表示沒有空間可寫數據,如果錯誤號是別的值,則表明發送失敗。如果發送緩沖區中有足夠空間或者是不足以拷貝所有待發送數據的空間的話,則拷貝前面N個能夠容納的數據,返回實際拷貝的位元組數。
而對於阻塞Socket而言,如果發送緩沖區沒有空間或者空間不足的話,write操作會直接阻塞住,如果有足夠空間,則拷貝所有數據到發送緩沖區,然後返回.
3、建立連接
阻塞方式下,connect首先發送SYN請求道伺服器,當客戶端收到伺服器返回的SYN的確認時,則connect返回.否則的話一直阻塞.
非阻塞方式,connect將啟用TCP協議的三次握手,但是connect函數並不等待連接建立好才返回,而是立即返回。返回的錯誤碼為EINPROGRESS,表示正在進行某種過程.
4、接收
連接
對於阻塞方式的傾聽socket,accept在連接隊列中沒有建立好的連接時將阻塞,直到有可用的連接,才返回。
非阻塞傾聽socket,在有沒有連接時都立即返回,沒有連接時,返回的錯誤碼為EWOULDBLOCK,表示本來應該阻塞
② 非阻塞方式socket send()返回大小和實際發送大小不一致
非阻塞模式是立即返回的,無法保證一次把包發完,所以發送時最好判斷發送位元組的多少,如果沒有發送完成,繼續調用發送介面發送
③ 伺服器編程心得(四)—— 如何將socket設置為非阻塞模式
在伺服器編程中,設置socket為非阻塞模式是提高性能的關鍵技術之一。不同操作系統上實現這一功能的方法有所差異。在Windows平台上,無論是使用socket()函數還是WSASocket()函數創建的socket都是默認為阻塞模式的。
相比之下,Linux平台上在使用socket()函數創建socket時,可以通過在type參數中設置SOCK_NONBLOCK標志來創建一個非阻塞模式的socket。例如:
在Linux上,通過將SOCK_NONBLOCK標志設置為SOCK_NONBLOCK,即可創建一個非阻塞模式的socket。
在Windows和Linux平台上的accept()函數返回的socket同樣是阻塞模式的,不過Linux額外提供了一個accept4()函數,該函數可以直接將返回的socket設置為非阻塞模式。實現方法只需將accept4()函數的最後一個參數flags設置為SOCK_NONBLOCK即可。
除了在創建socket時設置非阻塞模式外,還可以通過調用特定的API函數來實現。在Linux平台上,可以使用fcntl()或者ioctl()函數來修改socket的阻塞屬性。例如:
通過調用fcntl()函數或者ioctl()函數,即可將Linux平台上的socket設置為非阻塞模式。在設置非阻塞模式時,需要確保在接收和發送數據時使用了MSG_DONTWAIT標志,即在recv、recvfrom和send、sendto函數調用時,將flag參數設置為MSG_DONTWAIT。然而,根據Linux手冊的說明,設置recv()函數的flags標識位為MSG_DONTWAIT或者通過fcntl()函數設置O_NONBLOCK標識,已經足夠實現非阻塞操作,無需同時設定兩種方式。
在Windows平台上,可以通過調用ioctlsocket函數來改變socket的阻塞模式。將cmd參數設置為FIONBIO,同時將*argp參數設置為0或非0,即可分別設置socket為阻塞模式或非阻塞模式。需要注意的是,如果對socket調用了WSAAsyncSelect()或WSAEventSelect()函數後,再嘗試使用ioctlsocket()將socket設置為非阻塞模式,將會失敗。解決此問題,需要先通過設置lEvent參數為0或設置lNetworkEvents參數為0來禁用WSAAsyncSelect()或WSAEventSelect(),然後再調用ioctlsocket()設置socket為阻塞模式。
在實際項目中,有的前輩可能會在一個循環里調用fcntl()或者ioctlsocket()函數來改變socket的阻塞模式。然而,這是否必要,仍有待驗證。建議讀者根據實際需求和項目要求選擇合適的方法。
如果想系統地學習上述知識,推薦閱讀尹聖雨的《TCP/IP 網路編程》這本書,它兼顧了Windows和Linux兩個平台,使用C語言和操作系統的Socket API,能夠幫助讀者理解網路編程的基本概念和實現方法。對於更深入的高性能網路框架和設計,可以參考游雙老師的《Linux 高性能伺服器編程》一書。
此外,作者也出版了一本書《C++伺服器開發精髓》,涵蓋了從客戶端到伺服器、從Windows到Linux的經驗總結,包括C++開發編譯調試技術、多線程編程、網路故障排查、通信協議設計、高性能網路框架設計、服務框架設計、服務組件開發等知識。獲取更多書籍信息,請參考相關鏈接。