天天看點

通過UDP廣播實作Android區域網路Peer Discovering

TCP:Transmission Control Protocol(傳輸控制協定)

TCP是一種面向連接配接(連接配接導向)的、可靠的、基于位元組流的運輸層(Transport layer)通信協定,由IETF的RFC 793說明(specified)。TCP建立連接配接之後,通信雙方都同時可以進行資料的傳輸,是全雙工的。

在保證可靠性上,采用逾時重傳和捎帶确認機制;

在流量控制上,采用滑動視窗協定,協定中規定,對于視窗内未經确認的分組需要重傳;

在擁塞控制上,采用慢啟動算法。

TCP傳輸過程示意圖:

Client和Server建立連接配接之後,伺服器處于監聽狀态,即:伺服器端Socket并不定位具體的用戶端套接字,而是處于等待連接配接的狀态,實時監控網絡狀态,等待用戶端的連接配接請求。

用戶端Socket提出連接配接請求,要連接配接的目标是伺服器端Socket。為此,用戶端Socket必須首先描述它要連接配接的伺服器Socket,指出服務端Socket的位址和端口号,然後就向伺服器端Socket提出連接配接請求。

當伺服器端Socket監聽到或者說接收到用戶端Socket的連接配接請求時,就響應用戶端Socket的請求,建立一個新的線程,把伺服器端Socket的描述發給用戶端,一旦用戶端确認了此描述,雙方就正式通信。

而服務端Socket繼續處于監聽狀态,繼續接收其他用戶端Socket的連接配接請求。

TCP伺服器端代碼:

TCP用戶端代碼:

UDP:User Datagram Protocol(使用者資料包協定)

UDP是OSI參考模型中一種無連接配接的傳輸層協定,提供面向事務的簡單不可靠資訊傳送服務。它是IETF RFC 768是UDP的正式規範。

UDP協定的主要作用是将網絡資料流量壓縮成資料報的形式。

一個典型的資料報就是一個二進制資料的傳輸機關。

每一個資料報的前8個位元組用來包含報頭資訊,剩餘位元組則用來包含具體的傳輸資料。

相比于TCP,UDP在通信之前并不建立連接配接,UDP服務端Socket監聽某個端口的流量,用戶端Socket發送封包給服務端Socket指定端口,服務端Socket處理完資訊之後也并不回報資訊給用戶端Socket。

即:用戶端Socket發送封包後,不關心服務端是否收到封包;服務端Socket若收到封包,也并不告知用戶端Socket。

UDP伺服器端代碼:

UDP用戶端代碼:

總結下TCP和UDP的主要差別:

TCP

UDP

是否連接配接

面向連接配接

面向非連接配接

傳輸是否可靠

可靠

不可靠

速度

應用場景

要求準确性資料(例如金融、庫存)

不求準确,但求實時、快(語音、圖像資料)

單點傳播(unicast): 是指封包在計算機網絡的傳輸中,目的位址為單一目标的一種傳輸方式。它是現今網絡應用最為廣泛,通常所使用的網絡協定或服務大多采用單點傳播傳輸,例如一切基于TCP的協定。

每次隻有兩個實體互相通信,發送端和接收端都是唯一确定的。在IPv4網絡中,0.0.0.0到223.255.255.255屬于單點傳播位址。

你對小月月喊“小月月”,那麼隻有小月月回過頭來答應你。

多點傳播(multicast): 也叫多點傳播, 多點廣播或群播。 指把資訊同時傳遞給一組目的位址。它使用政策是最高效的,因為消息在每條網絡鍊路上隻需傳遞一次,而且隻有在鍊路分叉的時候,消息才會被複制。

“多點傳播”這個詞通常用來指代IP多點傳播。IP多點傳播是一種通過使用一個多點傳播位址将資料在同一時間以高效的方式發往處于TCP/IP網絡上的多個接收者的協定。此外,它還常用來與RTP等音視訊協定相結合。網際網路架構師戴夫·克拉克是這樣描述IP多點傳播的:“你把資料包從一頭放進去,網絡就會試圖将它們傳遞到想要得到它們的人那裡。”多點傳播封包的目的位址使用D類IP位址, D類位址不能出現在IP封包的源IP位址字段。在IPv4網絡中,224.0.0.0到239.255.255.255屬于多點傳播位址。

你在大街上大喊一聲“美女”, 會有一群女性回頭看你。

廣播(broadcast):是指封包在計算機網絡中傳輸時,目的位址為網絡中所有裝置的一種傳輸方式。實際上,這裡所說的“所有裝置”也是限定在一個範圍之中,稱為“廣播域”。

并非所有的計算機網絡都支援廣播,例如X.25網絡和幀中繼都不支援廣播,而且也沒有在“整個網際網路範圍中”的廣播。IPv6亦不支援廣播,廣播相應的功能由任播(anycast)代替。通常,廣播都是限制在區域網路中的,比如以太網或令牌環網絡。因為廣播在區域網路中造成的影響遠比在廣域網中小得多。

以太網和IPv4網都用全1的位址表示廣播,分别是ff:ff:ff:ff:ff:ff和255.255.255.255。

令牌環網絡使用IEEE 802.2控制域中的一個特殊值來表示廣播。

你在公司大喊一聲“放假了”, 全部同僚都會響應,大叫爽死了。

任播(anycast):是一種網絡尋址和路由的政策,使得資料可以根據路由拓樸來決定送到“最近”或“最好”的目的地。

任播是與單點傳播、廣播群組播不同的方式。

在單點傳播中,在網絡位址和網絡節點之間存在一一對應的關系。

在廣播群組播中,在網絡位址和網絡節點之間存在一對多的關系:每一個目的位址對應一群接收可以複制資訊的節點。

在任播中,在網絡位址和網絡節點之間存在一對多的關系:每一個位址對應一群接收節點,但在任何給定時間,隻有其中之一可以接收到傳送端來的資訊。在網際網路中,通常使用邊界網關協定來實作任播。

作為老闆,你在公司大喊一聲“開發組的過來一個人”, 總會有一個人灰溜溜去響應, 挨批還是發錢啊?

以上内容部分出自單點傳播,多點傳播(多點傳播),廣播以及任播。

幾個關鍵的類:

DatagramSocket

DatagramPacket

NetworkInterface

如果把DatagramSocket比作建立的港口碼頭,那麼DatagramPacket就是發送和接收資料的集裝箱。

接收構造函數

比如,要接收資料長度為1024的位元組,建構位元組緩存區<code>byte buf[] = new byte[1024]</code>,建立DatagramPacket隻需傳入<code>buf[]</code>和長度,實作接收長度為length的包。

發送構造函數

比如,要發送資料為<code>byte[] data</code>,構造函數需要位元組數組,數組長度,接收端位址(IP)和端口(Port),構造資料封包包用來把長度為length 的包傳送到指定宿主的指定的端口号。

主要方法

<code>getAddress()</code>傳回接收或發送此資料封包的機器的 IP 位址。

<code>getData()</code>傳回接收的資料或發送出的資料。

<code>getLength()</code>傳回發送出的或接收到的資料的長度。

<code>getPort()</code>傳回接收或發送該資料封包的遠端主機端口号。

此類表示用來發送和接收資料報包的套接字。資料報套接字是包投遞服務的發送或接收點。

不綁定位址及端口構造函數:<code>DatagramSocket()</code>建立資料報套接字。

用于發送封包的套接字,一般不指定特定端口及位址。

綁定端口構造函數:<code>DatagramSocket(int port)</code>建立資料報套接字并将其綁定到本地主機上的指定端口。

綁定位址與端口構造函數:<code>DatagramSocket(int port, InetAddress laddr)</code>建立資料報套接字,将其綁定到指定的本地位址。

關于0.0.0.0的意義,可參考:全零網絡IP位址0.0.0.0表示意義詳談

<code>receive(DatagramPacket p)</code>從此套接字接收資料報包。

<code>void send(DatagramPacket p)</code>從此套接字發送資料報包。

<code>bind(SocketAddress addr)</code>将此 DatagramSocket 綁定到特定的位址和端口。

<code>void close()</code>關閉此資料報套接字。

<code>void connect(InetAddress address, int port)</code>将套接字連接配接到此套接字的遠端位址。

<code>void connect(SocketAddress addr)</code>将此套接字連接配接到遠端套接字位址(IP 位址 + 端口号)。

<code>void disconnect()</code>斷開套接字的連接配接。

<code>getInetAddress()</code>傳回此套接字連接配接的位址。

<code>InetAddress getLocalAddress()</code>擷取套接字綁定的本地位址。

NetworkInterface是JDK1.4中添加的一個擷取網絡接口的類,該網絡接口既可以是實體的網絡接口,也可以是虛拟的網絡接口,而一個網絡接口通常由一個 IP 位址來表示。

既然 NetworkInterface 用來表示一個網絡接口,那麼如果可以獲得目前機器所有的網絡接口(包括實體的和虛拟的),然後篩選出表示區域網路的那個網絡接口,那就可以得到機器在區域網路内的 IP 位址。

NetworkInterface常用到的方法有兩個:

<code>getNetworkInterfaces()</code>用于擷取目前機器上所有的網絡接口;

<code>getInetAddresses()</code>用于擷取綁定到該網卡的所有的 IP 位址。

來看下這段代碼,實作的功能是周遊所有本地網絡接口,擷取廣播位址,并向它們發送廣播封包。

<code>getInterfaceAddresses</code>方法傳回的是一個綁定到該網絡接口的所有 InterfaceAddress 的集合。InterfaceAddress 是 JDK1.6 之後添加的類,包含 IP 位址(InetAddress),以及該位址對應的廣播位址和掩碼長度。

以上内容部分出自使用 NetworkInterface 獲得本機在區域網路内的 IP 位址。

在區域網路内通過UDP廣播實作Peer Discovering的方法非常簡單:

新加入區域網路的裝置發送廣播消息“我來了”;

其它已存在的裝置回複“知道了”。

整個流程如下圖所示:

是以,在初始化階段,首先要啟動一個廣播接收線程,用于接收指定端口的所有廣播流量:

在ServerSocket的構造函數中執行個體化<code>DatagramSocket</code>,指定端口,IP設定為0.0.0.0。

在接收線程的<code>run()</code>方法中,接收所有廣播消息:

如上圖所示,接收線程需要接收兩種廣播消息:“我來了”(<code>DISCOVER_REQUEST</code>)和“知道了”(<code>DISCOVER_RESPONSE</code>)。接收到<code>DISCOVER_REQUEST</code>後,發送<code>DISCOVER_RESPONSE</code>。需要注意的是:

這裡需要指定端口為<code>BROADCAST_PORT</code>,因為<code>DISCOVER_REQUEST</code>封包的的端口是随機的。不然無法在<code>BROADCAST_PORT</code>端口接收到<code>DISCOVER_RESPONSE</code>封包,新加入區域網路的裝置就無法感覺其他裝置的存在。

廣播發送線程在類的構造函數中初始化<code>DatagramSocket</code>:

在本demo中,發送通過<code>AsyncTask</code>進行實作,在background中發送消息,發送完成後通過Handler在界面Toast提示。

本文轉自xsster51CTO部落格,原文連結:http://blog.51cto.com/12945177/1929758,如需轉載請自行聯系原作者