Python中send()和sendall()的差別
估計每個學習
Python
網絡程式設計的人,都會遇到過這樣的問題:
-
和send()
到底有什麼差別?sendall()
-
和send()
原理是怎麼樣的?sendall()
-
和send()
能做什麼事情?sendall()
- 到底是使用
還是send()
?sendall()
看完下面的文章,應該就能明白了
知識補充
首先會對一些常見的網絡程式設計知識進行補充下:
MTU
通信術語 最大傳輸單元(
Maximum Transmission Unit,MTU
)是指一種通信協定的某一層上面所能通過的最大資料包大小(以位元組為機關)
以以太網傳送
IPv4
封包為例。
MTU
表示的長度包含IP標頭的長度,如果IP層以上的協定層發送的資料封包的長度超過了
MTU
,則在發送者的IP層将對資料封包進行分片,在接收者的IP層對接收到的分片進行重組。
TCP傳輸的可靠性
- 應用資料被分割成
認為最适合發送的資料塊(根據TCP
設定)。這和MTU
完全不同,應用程式産生的資料長度将保持不變。由UDP
傳遞給IP的資訊機關稱為封包段或段(TCP
)。segment
- 當
發出一個段後,它啟動一個定時器,等待目的端确認收到這個封包段。如果不能及時收到一個确認,将重發這個封包段。當TCP
收到發自TCP
連接配接另一端的資料,它将發送一個确認。TCP
有延遲确認的功能,在此功能沒有打開,則是立即确認。功能打開,則由定時器觸發确認時間點。TCP
-
将保持它首部和資料的檢驗和。這是一個端到端的檢驗和,目的是檢測資料在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP
将丢棄這個封包段和不确認收到此封包段(希望發端逾時并重發)。TCP
- 既然TCP封包段作為IP資料報來傳輸,而IP資料報的到達可能會失序,是以
封包段的到達也可能會失序。如果必要,TCP
将對收到的資料進行重新排序,将收到的資料以正确的順序交給應用層。TCP
- 既然IP資料報會發生重複,
的接收端必須丢棄重複的資料。TCP
-
還能提供流量控制。TCP
連接配接的每一方都有固定大小的緩沖空間。TCP
的接收端隻允許另一端發送接收端緩沖區所能接納的資料。這将防止較快主機緻使較慢主機的緩沖區溢出。TCP
send()
使用
send()
進行發送的時候,
Python
将内容傳遞給系統底層的
send
接口,也就是說,
Python
并不知道這次調用是否會全部發送完成,比如
MTU
是1500,但是此次發送的内容是2000,那麼除了標頭等等其他資訊占用,發送的量可能在1000左右,還有1000未發送完畢
但是,
send()
不會繼續發送剩下的包,因為它隻會發送一次,發送成功之後會傳回此次發送的位元組數,如上例,會傳回數字1000給使用者,然後就結束了
如果需要将剩下的1000發送完畢,需要使用者自行擷取傳回結果,然後将内容剩下的部分繼續調用
send()
進行發送
sendall()
sendall()
是對
send()
的包裝,完成了使用者需要手動完成的部分,它會自動判斷每次發送的内容量,然後從總内容中删除已發送的部分,将剩下的繼續傳給
send()
進行發送;
源碼
send()
是直接調用的系統底層接口,是以
Python
源碼沒有,隻有
C
的,由于不是很懂
C
,是以就沒有去找
C
源碼了
下面的源碼是從
pypy
中複制出來的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def send(self, data, flags=0): """Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent; this may be less than len(data) if the network is busy.""" with rffi.scoped_nonmovingbuffer(data) as dataptr: return self.send_raw(dataptr, len(data), flags) def sendall(self, data, flags=0, signal_checker=None): """Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it\'s impossible to tell how much data has been sent.""" with rffi.scoped_nonmovingbuffer(data) as dataptr: remaining = len(data) p = dataptr while remaining > 0: try: res = self.send_raw(p, remaining, flags) p = rffi.ptradd(p, res) remaining -= res except CSocketError, e: if e.errno != _c.EINTR: raise if signal_checker is not None: signal_checker() |
看完此段源碼,心中應該對
send()
和
sendall()
有一些認識了
什麼時候使用 send()
什麼時候使用 sendall()
send()
sendall()
一般情況下,我們都應該使用
sendall()
,除非自己弄懂了他們的原理,并且有必要在每次包發送之後進行一些必要的處理,不然我們都不需要去使用
send()
,而應該使用已經包裝好的
sendall()