標籤:組播 import blog ima sheng 實現 單播 微軟 測試的
一、說明1.1 標準組播解釋
通訊分為單播、多播(即組播)、廣播三種方式
單播指寄件者發送之後,IP資料包被路由器發往目的IP指定的唯一一台裝置的通訊形式,比如你現在與web伺服器通訊就是單播形式
廣播指寄件者發送之後,IP資料包被路由器發給與其串連的所有裝置的通訊形式
組播指寄件者發送之後,IP資料包被路由器發往目的IP對應組播組名下所有主機的通訊形式
1.2 個人理解組播解釋
對於標準的組播解釋,說明似乎還算是清楚的,但具體到技術就有很多問題。比如我將資料包發往一個組播地址,這個組播地址對應一台物理裝置嗎?如果不是一台物理裝置那誰依據什麼向哪些主機發送該資料包等等。
結合各資料和自己測試的情況總結出了以下幾點:
1) 編寫發送程式:組播資料包是且只是目的IP是組播保留地址的UDP資料包,與正常UDP資料包的區別只是其目的IP是組播保留IP
2) 發送資料包主機:網卡在看到目的IP是組播保留IP後,自動將目的mac地址改成組播mac地址然後向其各連接埠都發送出去
3) 交換器:交換器在接收到資料包之後,通過目的mac地址認識到這是一個組播資料包,修改源mac為自己mac、保持目的mac為組播mac不變向其各連接埠都發送出去(交換器對組播包的處理和廣播包應該是一樣的,或者說對於交換器只有單播包和廣播包)
4) 路由器:路由器在接收到資料包之後,通過目的mac地址或目的ip地址認識到這是一個組播資料包,修改源mac為自己mac、保持目的mac為組播mac不變,保持源IP不變、保持目的IP為組播保留IP不變,依照與路由表類似的“組播組地址表”向與目的IP匹配的一個或多連接埠將資料包發送出去
5) 接收資料包主機:接收資料包主機要想接收到發送主機發送的資料包,首先他要(向路由器說明)加入寄件者發往的組播組,然後他要在本地啟動一個進程監聽寄件者發往的連接埠
6) 組播需要硬體支援,有些路由器是不支援組播的,就直觀感受看如果在全球實現組播那維護“組播組地址表”會給路由器帶來很大負擔路由大廠商應該也不是很願意支援組播;也就是說理論上組播可以在廣域網路上實現,但其實一般只在區域網路中(能夠)使用。
通訊舉例如下:
寄件者S(假設使用組播地址為234.2.2.2,使用組播連接埠為23456)----寄件者向234.2.2.2:23456發送一個UDP資料包Packet1
接收者R1(假設其IP地址為192.168.220.128)----第一步通過setsockopt加入組播組(234.2.2.2);第二步啟動進程監聽192.168.220.128:23456
接收者R2(假設其IP地址為192.168.220.129)----第一步通過setsockopt加入組播組(234.2.2.2);第二步啟動進程監聽192.168.220.129:23456
最終效果----寄件者發往234.2.2.2:23456的udp資料包,R1的23456連接埠收到一份Packet1,R2的23456連接埠收到一份Packet1
1.3 誰是服務端引發的混亂
我在前面一直使用“寄件者”、“接收者”,而沒有使用“服務端”、“用戶端”,因為“服務端”和“用戶端”在組播中容易引發混亂。
在我們一般的socket編程中都是服務端去bind;但在組播中是反過來,用戶端(接收者)去bind,而寄件者(服務端)是不用去bind的(注意是不用而不是不能,你非要bind也是可以的,bind和不bind只是使用固定連接埠還是使用隨機連接埠的區別)。
有些小夥伴意識到了這個問題,為了與習慣一致,所以直接將寄件者稱為用戶端,接收者稱為服務端。
其實這種叫法是不合適的,以初高中常見的電腦課情境為例:老師控制所有電腦顯示老師的電腦操作,這時作為寄件者的老師電腦從認知上確實應該是服務端而不是用戶端才對。
如果你讀了半天沒聽懂這裡在說什麼,那不必在意,記得組播中盡裡使用“寄件者”和“接收者”,少用“服務端”和“用戶端”就對了。
二、程式實現2.1 程式碼
寄件者代碼:
import timeimport socket# 組播組IP和連接埠mcast_group_ip = ‘234.2.2.2‘mcast_group_port = 23456def sender(): # 建立發送socket,和正常UDP資料包沒區別 send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 每十秒發送一遍訊息 while True: message = "this message send via mcast !" # 發送寫法和正常UDP資料包的還是完全沒區別 # 猜測只可能是網卡自己在識別到目的ip是組播地址後,自動將目的mac地址設為多播mac地址 send_sock.sendto(message.encode(), (mcast_group_ip, mcast_group_port)) print(f‘{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: message send finish‘) time.sleep(10)if __name__ == "__main__": sender()
接收者代碼:
import structimport timeimport socket# 組播組IP和連接埠mcast_group_ip = ‘234.2.2.2‘mcast_group_port = 23456def receiver(): # 建立接收socket,和正常UDP資料包沒區別 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 擷取本地IP地址 local_ip = socket.gethostbyname(socket.gethostname()) # 監聽連接埠,已測試過其實可以直接bind 0.0.0.0;但注意不要bind 127.0.0.1不然其他機器發的組播包就收不到了 sock.bind((local_ip, mcast_group_port)) # 加入組播組 mreq = struct.pack("=4sl", socket.inet_aton(mcast_group_ip), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,mreq) # 允許連接埠複用,看到很多教程都有沒想清楚意義是什麼,我這裡直接注釋掉 # sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 設定非阻塞,看到很多教程都有也沒想清楚有什麼用,我這裡直接注釋掉 # sock.setblocking(0) while True: try: message, addr = sock.recvfrom(1024) print(f‘{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Receive data from {addr}: {message.decode()}‘) except : print("while receive message error occur")if __name__ == "__main__": receiver()
2.2 運行
寄件者:
接收者運行:
參考:
19285363
74938534?locationNum=10&fps=1
http://blog.sina.com.cn/s/blog_69c81c3e0102x9pm.html
Python3組播通訊編程實現教程(寄件者+接收者)