UDP相對於TCP來說,雖然是無連接,不可靠傳輸,但是可以實現組播,可以同時給多個主機發送數據,比如聊天室之類的應用,如果為每個用戶之間都建立一個Tcp連接,而且每次發送的數據又是相同的,這樣做使得程序開銷大,而且占用內存多,Udp還有一個廣播的服務,但是廣播不能篩選,也就是說廣播會向所有在同一子網的主機發送數據,這樣無疑也增加了網絡負擔,這時就可以通過Udp的組播來實現,最近一直在摸索中,下面對做一下組播的總解
組播的地址采用D類IP地址,范圍是從 224.0.0.0 到 239.255.255.255,下面有幾個保留地址,一般不作為用戶使用的地址
224.0.0.1 - 該子網上的所有主機。
224.0.0.2 - 該子網上的所有路由器。
224.0.0.5 - 開放最短路徑優先(Open Shortest Path First,OSPF)算法第2版,設計用於到達某個網絡上的所有OSPF路由器。
224.0.0.6 - 開放最短路徑優先算法第2版,設計用於到達某個網絡上的所有OSPF指定的路由器。
224.0.0.9 - 路由信息協議(Routing Information Protocol,RIP)第2版。
224.0.1.1 - 網絡時間協議(Network Time Protocol)。
過多詳細介紹見百度百科 http://baike.baidu.com/view/1871353.htm
對組播的使用:創建套接字,綁定本地地址,加入組播,監聽組播信息
1、首先定義一個套接字狀態信息,用於異步時保存信息
public class StateObject { public Socket sock; public byte[] buffer = new byte[1024]; public EndPoint endpoint; public StateObject(Socket sock, EndPoint endpoint) { this.sock = sock; this.endpoint = endpoint; } }
2、創建套接字,綁定本地地址,加入組播,監聽組播信息
private static IPAddress mcastAddress; private static int mcastPort; private static Socket mcastSocket; private static MulticastOption mcastOption; private void btnJoin_Click(object sender, EventArgs e) { //組播地址和端口 mcastAddress = IPAddress.Parse("224.168.100.2"); mcastPort = 8000; try { //組播套接字,綁定本地地址(組播端口) mcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(tbLocalIP.Text), mcastPort); mcastSocket.Bind(localEP); //加入組播 mcastOption = new MulticastOption(mcastAddress, IPAddress.Parse(tbLocalIP.Text)); mcastSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, mcastOption); //開始接收信息 byte[] bytes = new Byte[1024]; EndPoint remoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0); StateObject so = new StateObject(mcastSocket, remoteEP); mcastSocket.BeginReceiveFrom(so.buffer, 0, so.buffer.Length, SocketFlags.None, ref so.endpoint, ReceiveFromCallback, so); } catch (Exception) { } } private void ReceiveFromCallback(IAsyncResult ar) { StateObject so = ar.AsyncState as StateObject; try { int length = so.sock.EndReceiveFrom(ar, ref so.endpoint); string recvstr = System.Text.Encoding.UTF8.GetString(so.buffer, 0, length); MessageBox.Show(recvstr); //繼續接收消息 so.sock.BeginReceiveFrom(so.buffer, 0, so.buffer.Length, SocketFlags.None, ref so.endpoint, ReceiveFromCallback, so); } catch (SocketException) { //連接斷開 } catch (ObjectDisposedException) { //套接字關閉 } }
3、向組播發送信息,只要加入了該多播組的成員都能收到該信息
private void btnSendMsg_Click(object sender, EventArgs e) { try { IPEndPoint endPoint = new IPEndPoint(mcastAddress, mcastPort); mcastSocket.SendTo(System.Text.Encoding.UTF8.GetBytes("你好"), endPoint); } catch (SocketException) { //連接斷開 } catch (ObjectDisposedException) { //套接字關閉 } }
4、最后說下套接字屬性的設置問題,也就是SetSocketOption函數
該函數有四個重載,分別設置不同屬性,具體屬性定義在MSDN上有
http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.setsocketoption(v=VS.80).aspx
Socket.SetSocketOption (SocketOptionLevel, SocketOptionName, Boolean) 將指定的 Socket 選項設置為指定的 Boolean 值。
Socket.SetSocketOption (SocketOptionLevel, SocketOptionName, Byte[]) 將指定的 Socket 選項設置為指定的值,表示為字節數組。
Socket.SetSocketOption (SocketOptionLevel, SocketOptionName, Int32) 將指定的 Socket 選項設置為指定的整數值。
Socket.SetSocketOption (SocketOptionLevel, SocketOptionName, Object) 將指定的 Socket 選項設置為指定值,表示為對象。
具體屬性說明,具體屬性對於的SocketOptionLevel可以查看上面地址,不同的函數對於的設置又不一樣
http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketoptionname(v=vs.80).aspx
成員名稱 | 說明 | |
---|---|---|
![]() |
AcceptConnection | 套接字正在偵聽。 |
![]() |
AddMembership | 添加一個 IP 組成員。 |
![]() |
AddSourceMembership | 聯接源組。 |
![]() |
BlockSource | 阻止源中的數據。 |
![]() |
Broadcast | 允許在套接字上發送廣播消息。 |
![]() |
BsdUrgent | 使用 RFC-1222 中定義的緊急數據。此選項只能設置一次,而且設置以后就不能關閉。 |
![]() |
ChecksumCoverage | 設置或獲取 UDP 校驗和覆蓋。 |
![]() |
Debug | 記錄調試信息。 |
![]() |
DontFragment | 不對 IP 數據報進行分段。 |
![]() |
DontLinger | 完全關閉套接字,不做逗留。 |
![]() |
DontRoute | 不路由,將數據包直接發送到接口地址。 |
![]() |
DropMembership | 放置一個 IP 組成員。 |
![]() |
DropSourceMembership | 放置一個源組。 |
![]() |
Error | 獲取錯誤狀態並清除。 |
![]() |
ExclusiveAddressUse | 使套接字能夠為獨占訪問進行綁定。 |
![]() |
Expedited | 使用 RFC-1222 中定義的加急數據。此選項只能設置一次,而且設置以后就無法關閉。 |
![]() |
HeaderIncluded | 指示應用程序為輸出數據報提供 IP 頭。 |
HopLimit | 指定 Internet 協議版本 6 (IPv6) 數據包的最大路由器躍點數目。這類似於 Internet 協議版本 4 的生存時間 (TTL)。 | |
![]() |
IPOptions | 指定要插入到輸出數據報中的 IP 選項。 |
![]() |
IpTimeToLive | 設置 IP 頭生存時間字段。 |
![]() |
KeepAlive | 使用 keep-alive。 |
![]() |
Linger | 如果存在未發送的數據,則在關閉時逗留。 |
![]() |
MaxConnections | 不受支持;如果使用,將引發 SocketException。 |
![]() |
MulticastInterface | 為輸出的多路廣播數據包設置接口。 |
![]() |
MulticastLoopback | IP 多路廣播環回。 |
![]() |
MulticastTimeToLive | IP 多路廣播生存時間。 |
![]() |
NoChecksum | 發送校驗和設置為零的 UDP 數據報。 |
![]() |
NoDelay | 為發送合並禁用 Nagle 算法。 |
![]() |
OutOfBandInline | 接收正常數據流中的帶外數據。 |
![]() |
PacketInformation | 返回有關接收到的數據包的信息。 |
![]() |
ReceiveBuffer | 指定為接收保留的每個套接字緩沖區空間的總量。這與最大消息大小或 TCP 窗口的大小無關。 |
![]() |
ReceiveLowWater | 為 Receive 操作指定低水印。 |
![]() |
ReceiveTimeout | 接收超時。此選項只適用於同步方法,它對異步方法(如 BeginSend 方法)無效。 |
![]() |
ReuseAddress | 允許將套接字綁定到已在使用中的地址。 |
![]() |
SendBuffer | 指定為發送保留的每個套接字緩沖區空間的總量。這與最大消息大小或 TCP 窗口的大小無關。 |
![]() |
SendLowWater | 為 Send 操作指定低水印。 |
![]() |
SendTimeout | 發送超時。此選項只適用於同步方法,它對異步方法(如 BeginSend 方法)無效。 |
![]() |
Type | 獲取套接字類型。 |
![]() |
TypeOfService | 更改服務字段的 IP 頭類型。 |
![]() |
UnblockSource | 取消阻止先前被阻止的源。 |
UpdateAcceptContext | 使用現有套接字的屬性更新已接受套接字的屬性。這等效於使用 Winsock2 SO_UPDATE_ACCEPT_CONTEXT 套接字選項,並且僅在面向連接的套接字上受支持。 | |
UpdateConnectContext | 使用現有套接字的屬性更新已連接套接字的屬性。這等效於使用 Winsock2 SO_UPDATE_CONNECT_CONTEXT 套接字選項,並且僅在面向連接的套接字上受支持。 | |
![]() |
UseLoopback | 可能時避開硬件。 |
例如在使用Udp廣播數據的時候,需要設置Broadcast屬性為true
在函數 Socket.SetSocketOption (SocketOptionLevel, SocketOptionName, Boolean)中
Broadcast屬性對應的 SocketOptionLevel 為 Socket
則調用為
sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);