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);