UDPClient 類使用 UDP 與網絡服務通訊。UDP 的優點是簡單易用,並且能夠同時向多個地址廣播消息。但由於 UDP 協議是一個無連接協議,因此發送到遠程終結點的 UDP 數據文報不一定能夠到達,也不一定能夠以發送的相同順序到達。使用 UDP 的應用程序必須准備處理丟失的和順序有誤的數據文報。
若要使用 UDP 發送數據文報,必須知道承載所需服務的網絡設備的網絡地址以及該服務用於通訊的 UDP 端口號。
特殊網絡地址用於支持基於 IP 的網絡上的 UDP 廣播消息。下面探討的內容以 Internet 上使用的 IP 版本 4 地址族作為示例。
IP 版本 4 地址使用 32 位指定網絡地址。對於使用 255.255.255.0 網絡掩碼的 C 類地址,這些位被分為四個八位字節。當以十進制數表示時,這四個八位字節構成熟悉的以點分隔的四部分表示法,如 192.168.100.2。前兩個八位字節(此示例中為 192.168)構成網絡號;第三個八位字節 (100) 定義子網;最後一個八位字節 (2) 是主機標識符。
將 IP 地址的所有位均設置為 1(即 255.255.255.255)可構成有限的廣播地址。將 UDP 數據文報發送到此地址可將消息傳遞到該廣播網絡上的任何主機。由於路由器從不轉發發送到此地址的消息,因此只有已連接的網絡上的主機才可看到這些廣播。
通過將部分地址的所有位全都設置為 1,可以將廣播定向到特定的網絡部分。例如,若要將廣播發送到以 192.168 打頭的 IP 地址標識的網絡上的所有主機,請將地址的子網和主機部分全都設置為 1,如 192.168.255.255。若要將廣播限制在單個子網,則只將主機部分設置全都為 1,如 192.168.100.255。
UdpClient 類可向任何網絡廣播地址廣播,但它無法偵聽發送到網絡的廣播。必須使用 Socket 類才能偵聽網絡廣播。
當所有接收者都位於單個網絡中時,或者當許多客戶端需要接收廣播時,廣播地址將起作用。當接收者為網絡的一小部分時,應將消息發送到多路廣播組,在那裡只有加入此組的客戶端才能接收到消息。范圍從 224.0.0.2 到 244.255.255.255 的 IP 地址保留為主機組地址。IP 號 224.0.0.0 被保留,而 224.0.0.1 分配給所有 IP 主機的固定組。
下面的示例使用 UdpClient 偵聽端口8080 上的多路廣播地址組 10.0.0.1 的 UDP 數據文報廣播。它接收消息字符串並將消息寫入控制台。
例:在IDE新建一個工程項目,窗體類命名為FormChat,在窗體上放一個控件ListBox,用於接收消息,放一個TextBox控件用於輸入要發送的消息,放一個Button用於發送消息。全源代碼如下:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace WinUDPChat
{
///
/// Form1 的摘要說明。
///
public class FormChat : System.Windows.Forms.Form
{
///
/// 必需的設計器變量。
///
private System.ComponentModel.Container components = null;
private static UdpClient m_Client;
//private static UdpClient m_Server;
private static int ListenerPort = 8080;
private static int SenderPort = 8080;
private static int LocalPort;
private static int RemotePort;
private static string m_szHostName;
private static IPAddress m_GroupAddress_C;
private static IPAddress m_GroupAddress_S;
private static IPHostEntry m_LocalHost;
private static IPEndPoint m_RemoteEP;
private System.Windows.Forms.Button button_sendMSG;
private System.Windows.Forms.TextBox textBox_msg;
private System.Windows.Forms.ListBox listBox_msg;
private Thread th;
public FormChat()
{
//
// Windows 窗體設計器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 調用後添加任何構造函數代碼
//
}
///
/// 清理所有正在使用的資源。
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗體設計器生成的代碼
///
/// 設計器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內容。
///
private void InitializeComponent()
{
this.button_sendMSG = new System.Windows.Forms.Button();
this.textBox_msg = new System.Windows.Forms.TextBox();
this.listBox_msg = new System.Windows.Forms.ListBox();
this.SuspendLayout();
//
// button_sendMSG
//
this.button_sendMSG.Location = new System.Drawing.Point(96, 234);
this.button_sendMSG.Name = "button_sendMSG";
this.button_sendMSG.TabIndex = 0;
this.button_sendMSG.Text = "發送";
this.button_sendMSG.Click += new System.EventHandler(this.button_sendMSG_Click);
//
// textBox_msg
//
this.textBox_msg.Location = new System.Drawing.Point(80, 197);
this.textBox_msg.Name = "textBox_msg";
this.textBox_msg.TabIndex = 1;
this.textBox_msg.Text = "";
//
// listBox_msg
//
this.listBox_msg.ItemHeight = 12;
this.listBox_msg.Location = new System.Drawing.Point(21, 22);
this.listBox_msg.Name = "listBox_msg";
this.listBox_msg.Size = new System.Drawing.Size(248, 136);
this.listBox_msg.TabIndex = 2;
//
// FormChat
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.listBox_msg);
this.Controls.Add(this.textBox_msg);
this.Controls.Add(this.button_sendMSG);
this.Name = "FormChat";
this.Text = "聊天室";
this.Load += new System.EventHandler(this.FormChat_Load);
this.Closed += new System.EventHandler(this.FormChat_Closed);
this.ResumeLayout(false);
}
#endregion
///
/// 應用程序的主入口點。
///
[STAThread]
static void Main()
{
Application.Run(new FormChat());
}
private void FormChat_Load(object sender, System.EventArgs e)
{
LocalPort = SenderPort;
RemotePort = ListenerPort;
m_szHostName = Dns.GetHostName();
m_LocalHost = Dns.GetHostByName(m_szHostName);
Initialize();
th = new Thread(new ThreadStart(Listener));
th.Start();
}
public void Terminate()
{
m_Client.DropMulticastGroup(m_GroupAddress_C);
}
public void Initialize()
{
//
// 實例化 UdpCLient
//
m_Client = new UdpClient(LocalPort);
//
// 創建對方主機的終結點
//
m_GroupAddress_S = IPAddress.Parse("10.0.0.1"); //要發送到的計算機IP
m_RemoteEP = new IPEndPoint( m_GroupAddress_S, RemotePort );
}
public void Listener()
{
//
// 創建多路廣播組對象
//
System.Net.IPHostEntry localhost =Dns.GetHostByName(Dns.GetHostName());
string local_IP= localhost.AddressList[0].ToString();//接收消息的本地IP,用於監聽
m_GroupAddress_C = IPAddress.Parse(local_IP);
//
// 聯接組
//
try
{
m_Client.JoinMulticastGroup(m_GroupAddress_C, 100); //不添加到多路廣播組也可以
}
catch(Exception err)
{
throw(err);//無法聯接多路廣播組
}
//
// 偵聽器等待數據到來
// 並用緩沖區保存它。
Thread.Sleep(2000); // 確保 client2 正在接收
Encoding ASCII = Encoding.ASCII;
// while(!m_Done)
while(true)
{
IPEndPoint endpoint = null;
//endpoint = new IPEndPoint(m_GroupAddress_C,LocalPort);//這句代碼不要也可以
Byte[] data = m_Client.Receive(ref endpoint);
String strData = ASCII.GetString(data);
listBox_msg.Items.Add(strData);
}
}
private void FormChat_Closed(object sender, System.EventArgs e)
{
try
{
try
{
if(th!=null)
{
if(th.IsAlive)
{
th.Abort();
}
th=null;
}
}
catch
{
try
{
System.Threading.Thread.ResetAbort();
}
catch
{}
}
Terminate();
m_Client.Close();
}
catch
{
}
}
private void button_sendMSG_Click(object sender, System.EventArgs e)
{
Byte [] buffer = null;
Encoding ASCII = Encoding.ASCII;
string s = textBox_msg.Text;
buffer = new Byte[s.Length + 1];
//
// 將數據發送給遠程對方主機
//
int len = ASCII.GetBytes( s.ToCharArray(), 0, s.Length, buffer, 0);
int ecode = m_Client.Send(buffer, len, m_RemoteEP);
}
}
}