C# UDP 入门

本文详细介绍了UDP协议的基本概念、特点及应用场景。涵盖了UDP报头结构、端口号分配、数据报长度和校验值等内容,并提供了使用Socket和UdpClient进行UDP通信的示例代码。

UDP 是User Datagram Protocol的简称, 中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。它是IETF RFC 768是UDP的正式规范。

UDP报头

  UDP报头由4个域组成,其中每个域各占用2个字节,具体如下:

  源端口号

  目标端口号

  数据报长度

  校验值

  UDP协议使用端口号为不同的应用保留其各自的数据传输通道。UDP和TCP协议正是采用这一机制实现对同一时刻内多项应用同时发送和接收数据的支持。数据发送一方(可以是客户端或服务器端)将UDP数据报通过源端口发送出去,而数据接收一方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。一般来说,大于49151的端口号都代表动态端口。

  数据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到8192字节。

  UDP协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。这与TCP协议是不同的,后者要求必须具有校验值。

  许多链路层协议都提供错误检查,包括流行的以太网协议,也许想知道为什么UDP也要提供检查和。其原因是链路层以下的协议在源端和终端之间的某些通道可能不提供错误检测。虽然UDP提供有错误检测,但检测到错误时,UDP不做错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。

UDP协议的几个特性

  (1) UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

  (2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。

  (3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。

  (4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。

  (5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。

  (6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。 

  虽然UDP是一个不可靠的协议,但它是分发信息的一个理想协议。例如,在屏幕上报告股票市场、在屏幕上显示航空信息等等。UDP也用在路由信息协议RIP(Routing Information Protocol)中修改路由表。在这些应用场合下,如果有一个消息丢失,在几秒之后另一个新的消息就会替换它。UDP广泛用在多媒体应用中,例如,Progressive Networks公司开发的RealAudio软件,它是在因特网上把预先录制的或者现场音乐实时传送给客户机的一种软件,该软件使用的RealAudio audio-on-demand protocol协议就是运行在UDP之上的协议,大多数因特网电话软件产品也都运行在UDP之上。

  UDP = uridine diphosphate,尿苷二磷酸,一种嘧啶核苷酸,由碱基尿嘧啶核糖组成,主要用途是RNA合成(转录)时的原料。 另外UDP也是DTP能量消耗后产物,功能类似ADP,但较ADP少见。参与微生物肽聚糖等的合成。

我们先使用Socket的方法来实现

服务器代码:

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace UDP
{
class Program
{
static void Main( string []args)
{
int recv;
byte []data = new byte [ 1024 ];


// 得到本机IP,设置TCP端口号
IPEndPointip = new IPEndPoint(IPAddress.Any, 8001 );
Socketnewsock
= new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);

// 绑定网络地址
newsock.Bind(ip);

Console.WriteLine(
" ThisisaServer,hostnameis{0} " ,Dns.GetHostName());

// 等待客户机连接
Console.WriteLine( " Waitingforaclient " );

// 得到客户机IP
IPEndPointsender = new IPEndPoint(IPAddress.Any, 0 );
EndPointRemote
= (EndPoint)(sender);
recv
= newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(
" Messagereceivedfrom{0}: " ,Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data,
0 ,recv));

// 客户机连接成功后,发送信息
string welcome = " 你好! " ;

// 字符串与字节数组相互转换
data = Encoding.ASCII.GetBytes(welcome);

// 发送信息
newsock.SendTo(data,data.Length,SocketFlags.None,Remote);
while ( true )
{
data
= new byte [ 1024 ];
// 发送接受信息
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(Encoding.ASCII.GetString(data,
0 ,recv));
newsock.SendTo(data,recv,SocketFlags.None,Remote);
}
}

}
}

客户端代码 :

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace UDPClient
{
class Program
{
static void Main( string []args)
{
byte []data = new byte [ 1024 ];
string input,stringData;

// 构建TCP服务器
Console.WriteLine( " ThisisaClient,hostnameis{0} " ,Dns.GetHostName());

// 设置服务IP,设置TCP端口号
IPEndPointip = new IPEndPoint(IPAddress.Parse( " 127.0.0.1 " ), 8001 );

// 定义网络类型,数据连接类型和网络协议UDP
Socketserver = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);

string welcome = " 你好! " ;
data
= Encoding.ASCII.GetBytes(welcome);
server.SendTo(data,data.Length,SocketFlags.None,ip);
IPEndPointsender
= new IPEndPoint(IPAddress.Any, 0 );
EndPointRemote
= (EndPoint)sender;

data
= new byte [ 1024 ];
// 对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制
int recv = server.ReceiveFrom(data, ref Remote);
Console.WriteLine(
" Messagereceivedfrom{0}: " ,Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data,
0 ,recv));
while ( true )
{
input
= Console.ReadLine();
if (input == " exit " )
break ;
server.SendTo(Encoding.ASCII.GetBytes(input),Remote);
data
= new byte [ 1024 ];
recv
= server.ReceiveFrom(data, ref Remote);
stringData
= Encoding.ASCII.GetString(data, 0 ,recv);
Console.WriteLine(stringData);
}
Console.WriteLine(
" StoppingClient. " );
server.Close();
}

}
}

下面咱们再来使用UdpClient 来实现他们之间的通信

服务器端代码

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class Custom{

private static readonly IPAddressGroupAddress = IPAddress.Parse( " IP地址 " );
private const int GroupPort = 11000 ;
private static void StartListener(){
bool done = false ;

UdpClientlistener
= new UdpClient();
IPEndPointgroupEP
= new IPEndPoint(GroupAddress,GroupPort);

try {
listener.JoinMulticastGroup(GroupAddress);
listener.Connect(groupEP);

while ( ! done){
Console.WriteLine(
" Waitingforbroadcast " );
byte []bytes = listener.Receive( ref groupEP);

Console.WriteLine(
" Receivedbroadcastfrom{0}:/n{1}/n " ,
groupEP.ToString(),
Encoding.ASCII.GetString(bytes,
0 ,bytes.Length));
}

listener.Close();

}
catch (Exceptione){
Console.WriteLine(e.ToString());
}

}

public static int Main(String[]args){
StartListener();

return 0 ;
}
}

客户端代码:

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class Client{

private static IPAddressGroupAddress = IPAddress.Parse( " IP地址 " );
private static int GroupPort = 11000 ;

private static void Send(Stringmessage){
UdpClientsender
= new UdpClient();
IPEndPointgroupEP
= new IPEndPoint(GroupAddress,GroupPort);

try {
Console.WriteLine(
" Sendingdatagram:{0} " ,message);
byte []bytes = Encoding.ASCII.GetBytes(message);

sender.Send(bytes,bytes.Length,groupEP);

sender.Close();

}
catch (Exceptione){
Console.WriteLine(e.ToString());
}

}

public static int Main(String[]args){
Send(args[
0 ]);

return 0 ;
}
}

如果您是采用窗体的形式建议使用这种格式,否则在接收数据时可能会出现死机的现象

代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />--> // 创建一个子线程

Threadthread
= new Thread(
delegate ()
{
try
{
// 在这里写你的代码
}
catch (Exception)
{

}
}
);

thread.Start();

如有转载请注明出处谢谢合作!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值