1.UDP套接字
udp服務端:
1 ss = socket() #創建一個服務器的套接字 2 ss.bind() #綁定服務器套接字 3 inf_loop: #服務器無限循環 4cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送) 5 ss.close() # 關閉服務器套接字
udp客戶端:
1 cs = socket() # 創建客戶套接字 2 comm_loop: # 通訊循環 3 cs.sendto()/cs.recvfrom() # 對話(發送/接收) 4 cs.close() # 關閉客戶套接字
2.recv與recvfrom的區別:
part1:
發消息都是將數據發送到己端發送緩沖中,手消息都是從己端的緩沖區中收
tcp:send發消息,recv收消息
udp:sendto發消息,recvfrom收消息
part2:
tcp是基於數據流的,而udp是基於數據報的
send(bytes_data):發送數據流,數據流bytes_data若為空,自己這段的緩沖區也為空,操作系統不會控制tcp協議發空包
sendinto(bytes_data,ip_port):發送數據報,bytes_data為空,還有ip_port,所有即便是發送空的butes_data,數據報其實也不是空的,自己這端的緩沖區收到內容,操作系統就會控制udp協議發包.
part3:
1.tcp協議:
(1)如果收消息緩沖區里的數據為空,那么recv就會阻塞(阻塞很簡單,就是一直在等着接收)
(2)只不過tcp協議的客戶端send一個空數據就是真的空數據,客戶端即使有無窮個send空,也跟沒有一個樣.
(3)tcp基於鏈接通信
*基於鏈接,則需要listen(backlog),指定半連接池的大小
*基於鏈接,必須先運行的服務端,然后客戶端發起鏈接請求
*對於Mac空系統:如果一段斷開了鏈接,那另外一端的鏈接也跟着完蛋recv將不會阻塞,收到的是空(解決方法是:服務端在收消息后加上if判斷,空消息就break掉通信循環)
*對於Windows/Linux系統:如果一端斷開了鏈接,那另外一端的鏈接也跟着完蛋recv將不會阻塞,收到的是空(解決方法:服務端通信循環內加異常處理,捕捉到異常后就break掉通訊循環)
2.udp協議
(1)如果收消息緩沖區里的數據為"空",recvfrom也會阻塞
(2)支部會udp協議的客戶端sendinto一個空數據並不是真的空數據(包含:空數據+地址信息,得到的報仍然不會為空),所以客戶端只要有一個sendinto(不管是否發送空數據,都不會真的空數據),服務端就可以recvfrom到數據.
(3)udp無鏈接
*無鏈接,因而無需listen(backlog),更加沒有什么連接池之說了
*無鏈接,udp的sendinto不用管是否有一個正在運行的服務端,可以己端一個勁的發消息,只不過數據丟失
*recvfrom收的數據小於sendinto發送的數據時,在Mac和Linux系統上數據直接丟失,在Windows系統上發送的比接受的大直接報錯
*只有sendinto發送數據沒有recvfrom收數據,數據丟失
PS:
1.你單獨運行上面的udp的客戶端,你發現並不會報錯,相反tcp卻會報錯,因為udp協議只負責把包發出去,對方收不收,我根本管不着,而tcp是基於鏈接的,必須有一個服務端先運行着,客戶端去跟服務端建立連接然后依托於連接才能傳遞消息,任何一方試圖把連接摧毀都會導致對方程序崩潰
2.上面的udp程序,你注釋任何一條客戶端的sendinto,服務端都會卡住,為什么?因為服務端有幾個recvfrom就要對應幾個sendinto,哪怕是sendinto(b'')那也要有.
3.粘包現象:
只有TCP有粘包現象,UDP永遠不會粘包!
所謂的粘包問題主要還是因為接收對方不知道消息之間的界限,不知道一次性提取多少字節的數據造成的.
以下情況會發生粘包:
1.發送端需要等緩沖區滿才發送出去,造成粘包(發送數據時間間隔很短,數據量很小,會合到一起,產生粘包)
2.接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送一端數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包)
4.解決粘包方法:
struct模塊
該模塊可以把一個類型,如數字,轉成固定長度的bytes
>>>Struct.pack('i',111111111111)
struct.error:'i' format requires -2147483648 <= number <=2147483647 #這個是范圍