1 UDP 是User Datagram Protocol的简称, 中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。它是IETF RFC 768是UDP的正式规范。 UDP报头 2 3 UDP报头由4个域组成,其中每个域各占用2个字节,具体如下: 4 5 6 源端口号 7 8 目标端口号 9 10 数据报长度 11 12 校验值 13 14 UDP协议使用端口号为不同的应用保留其各自的数据传输通道。UDP和TCP协议正是采用这一机制实现对同一时刻内多项应用同时发送和接收数据的支持。数据发送一方(可以是客户端或服务器端)将UDP数据报通过源端口发送出去,而数据接收一方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。一般来说,大于49151的端口号都代表动态端口。 15 16 数据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到8192字节。 17 18 UDP协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。这与TCP协议是不同的,后者要求必须具有校验值。 19 20 21 许多链路层协议都提供错误检查,包括流行的以太网协议,也许想知道为什么UDP也要提供检查和。其原因是链路层以下的协议在源端和终端之间的某些通道可能不提供错误检测。虽然UDP提供有错误检测,但检测到错误时,UDP不做错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。 22 23 UDP协议的几个特性 24 25 26 (1) UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。 27 28 (2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。 29 30 (3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。 31 32 (4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。 33 34 (5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。 35 36 (6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。 37 38 虽然UDP是一个不可靠的协议,但它是分发信息的一个理想协议。例如,在屏幕上报告股票市场、在屏幕上显示航空信息等等。UDP也用在路由信息协议RIP(Routing Information Protocol)中修改路由表。在这些应用场合下,如果有一个消息丢失,在几秒之后另一个新的消息就会替换它。UDP广泛用在多媒体应用中,例如,Progressive Networks公司开发的RealAudio软件,它是在因特网上把预先录制的或者现场音乐实时传送给客户机的一种软件,该软件使用的RealAudio audio-on-demand protocol协议就是运行在UDP之上的协议,大多数因特网电话软件产品也都运行在UDP之上。 39 40 1.我们先使用Socket的方法来实现 41 42 服务器代码 43 44 using System; 45 using System.Collections.Generic; 46 using System.Text; 47 using System.Net; 48 using System.Net.Sockets; 49 namespace UDP 50 { 51 class Program 52 { 53 static void Main(string[] args) 54 { 55 int recv; 56 byte[] data = new byte[1024]; 57 58 //得到本机IP,设置TCP端口号 59 IPEndPoint ip = new IPEndPoint(IPAddress.Any , 8001); 60 Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram , ProtocolType.Udp); 61 //绑定网络地址 62 newsock.Bind(ip); 63 Console.WriteLine("This is a Server, host name is {0}",Dns.GetHostName()); 64 //等待客户机连接 65 Console.WriteLine("Waiting for a client"); 66 //得到客户机IP 67 IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); 68 EndPoint Remote = (EndPoint)(sender); 69 recv = newsock.ReceiveFrom(data, ref Remote); 70 Console .WriteLine ("Message received from {0}: ", Remote.ToString ()); 71 Console .WriteLine (Encoding .ASCII .GetString (data ,0,recv )); 72 //客户机连接成功后,发送信息 73 string welcome = "你好 ! "; 74 //字符串与字节数组相互转换 75 data = Encoding .ASCII .GetBytes (welcome ); 76 //发送信息 77 newsock .SendTo (data ,data.Length ,SocketFlags .None ,Remote ); 78 while (true ) 79 { 80 data =new byte [1024]; 81 //发送接受信息 82 recv =newsock.ReceiveFrom(data ,ref Remote); 83 Console .WriteLine (Encoding .ASCII .GetString (data ,0,recv)); 84 newsock .SendTo (data ,recv ,SocketFlags .None ,Remote ); 85 } 86 } 87 } 88 } 89 90 91 客户端代码 : 92 93 using System; 94 using System.Collections.Generic; 95 using System.Linq; 96 using System.Text; 97 using System.Net; 98 using System.Net.Sockets; 99 namespace UDPClient 100 { 101 class Program 102 { 103 static void Main(string[] args) 104 { 105 byte[] data = new byte[1024]; 106 string input ,stringData; 107 //构建TCP 服务器 108 Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName()); 109 //设置服务IP,设置TCP端口号 110 IPEndPoint ip = new IPEndPoint(IPAddress .Parse ("127.0.0.1") , 8001); 111 //定义网络类型,数据连接类型和网络协议UDP 112 Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 113 string welcome = "你好! "; 114 data = Encoding.ASCII.GetBytes(welcome); 115 server.SendTo(data, data.Length, SocketFlags.None, ip); 116 IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); 117 EndPoint Remote = (EndPoint)sender; 118 data = new byte[1024]; 119 //对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制 120 int recv = server.ReceiveFrom(data, ref Remote); 121 Console.WriteLine("Message received from {0}: ", Remote.ToString()); 122 Console.WriteLine(Encoding .ASCII .GetString (data,0,recv)); 123 while (true) 124 { 125 input = Console .ReadLine (); 126 if (input =="exit") 127 break ; 128 server .SendTo (Encoding .ASCII .GetBytes (input ),Remote ); 129 data = new byte [1024]; 130 recv = server.ReceiveFrom(data, ref Remote); 131 stringData = Encoding.ASCII.GetString(data, 0, recv); 132 Console.WriteLine(stringData); 133 } 134 Console .WriteLine ("Stopping Client."); 135 server .Close (); 136 } 137 } 138 } 139 140 141 142 2.下面咱们再来使用UdpClient 来实现他们之间的通信服务器端代码 143 144 using System; 145 using System.Net; 146 using System.Net.Sockets; 147 using System.Text; 148 149 public class Custom { 150 151 private static readonly IPAddress GroupAddress = IPAddress.Parse("IP地址"); 152 private const int GroupPort = 11000; 153 private static void StartListener() { 154 bool done = false; 155 156 UdpClient listener = new UdpClient(); 157 IPEndPoint groupEP = new IPEndPoint(GroupAddress,GroupPort); 158 159 try { 160 listener.JoinMulticastGroup(GroupAddress); 161 listener.Connect(groupEP); 162 163 while (!done) { 164 Console.WriteLine("Waiting for broadcast"); 165 byte[] bytes = listener.Receive( ref groupEP); 166 167 Console.WriteLine("Received broadcast from {0} : {1} ", 168 groupEP.ToString(), 169 Encoding.ASCII.GetString(bytes,0,bytes.Length)); 170 } 171 172 listener.Close(); 173 174 } catch (Exception e) { 175 Console.WriteLine(e.ToString()); 176 } 177 178 } 179 180 public static int Main(String[] args) { 181 StartListener(); 182 183 return 0; 184 } 185 } 186 187 188 客户端代码: 189 190 using System; 191 using System.Net; 192 using System.Net.Sockets; 193 using System.Text; 194 195 public class Client { 196 197 private static IPAddress GroupAddress = IPAddress.Parse("IP地址"); 198 private static int GroupPort = 11000; 199 200 private static void Send( String message) { 201 UdpClient sender = new UdpClient(); 202 IPEndPoint groupEP = new IPEndPoint(GroupAddress,GroupPort); 203 204 try { 205 Console.WriteLine("Sending datagram : {0}", message); 206 byte[] bytes = Encoding.ASCII.GetBytes(message); 207 208 sender.Send(bytes, bytes.Length, groupEP); 209 210 sender.Close(); 211 212 } catch (Exception e) { 213 Console.WriteLine(e.ToString()); 214 } 215 216 } 217 218 public static int Main(String[] args) { 219 Send(args[0]); 220 221 return 0; 222 } 223 } 224 225 226 如果您是采用窗体的形式建议使用这种格式,否则在接收数据时可能会出现死机的现象 227 // 创建一个子线程 228 Thread thread = new Thread( 229 delegate() 230 { 231 try 232 { 233 //在这里写你的代码 234 } 235 catch (Exception ) 236 { 237 } 238 } 239 ); 240 thread.Start(); 241