1. 程式人生 > >[C# 網絡編程系列]專題七:UDP編程補充——UDP廣播程序的實現

[C# 網絡編程系列]專題七:UDP編程補充——UDP廣播程序的實現

tca ssa 程序 als learn ans targe focus ase

上次因為時間的關系,所以把上一個專題遺留下的一個問題在本專題中和大家分享下,本專題主要介紹下如何實現UDP廣播的程序,下面就直接介紹實現過程和代碼以及運行的結果。

一、程序實現

UDP廣播程序的實現代碼:

[csharp] view plain copy print?
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Windows.Forms;
  7. namespace UDPBroadcast
  8. {
  9. /// <summary>
  10. /// 在界面上,用戶可以設置本地進程的IP地址和端口號,並將地址加入某個組播組;
  11. /// 可以輸入發送消息的目的組的地址,並且勾選“廣播”復選框將采用廣播的方式發送信息
  12. /// 在界面上點擊“接受按鈕”就啟動接收線程,這樣程序就可以接收廣播或組播的信息
  13. /// </summary>
  14. public partial class UdpBroadcasefrm : Form
  15. {
  16. private UdpClient sendUdpClient;
  17. private UdpClient receiveUdpClient;
  18. // 組播IP地址
  19. IPEndPoint broadcastIpEndPoint;
  20. public UdpBroadcasefrm()
  21. {
  22. InitializeComponent();
  23. IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
  24. tbxlocalip.Text = ips[5].ToString();
  25. tbxlocalport.Text = "8002";
  26. // 默認組,組播地址是有範圍
  27. // 具體關於組播和廣播的介紹參照我上一篇博客UDP編程
  28. // 本地組播組
  29. tbxGroupIp.Text = "224.0.0.1";
  30. // 發送到的組播組
  31. tbxSendToGroupIp.Text = "224.0.0.1";
  32. }
  33. // 設置加入組
  34. private void chkbxJoinGtoup_Click(object sender, EventArgs e)
  35. {
  36. if (chkbxJoinGtoup.Checked == true)
  37. {
  38. tbxGroupIp.Enabled = false;
  39. }
  40. else
  41. {
  42. tbxGroupIp.Enabled = true;
  43. tbxGroupIp.Focus();
  44. }
  45. }
  46. // 選擇發送模式後設置
  47. private void chkbxBroadcast_Click(object sender, EventArgs e)
  48. {
  49. if (chkbxBroadcast.Checked == true)
  50. {
  51. tbxSendToGroupIp.Enabled = false;
  52. }
  53. else
  54. {
  55. tbxSendToGroupIp.Enabled = true;
  56. tbxSendToGroupIp.Focus();
  57. }
  58. }
  59. // 發送消息
  60. private void btnSend_Click(object sender, EventArgs e)
  61. {
  62. if (tbxMessageSend.Text == "")
  63. {
  64. MessageBox.Show("消息內容不能為空!","提示");
  65. return;
  66. }
  67. // 根據選擇的模式發送信息
  68. if (chkbxBroadcast.Checked == true)
  69. {
  70. // 廣播模式(自動獲得子網中的IP廣播地址)
  71. broadcastIpEndPoint = new IPEndPoint(IPAddress.Broadcast, 8002);
  72. }
  73. else
  74. {
  75. // 組播模式
  76. broadcastIpEndPoint = new IPEndPoint(IPAddress.Parse(tbxSendToGroupIp.Text), 8002);
  77. }
  78. // 啟動發送線程發送消息
  79. Thread sendThread = new Thread(SendMessage);
  80. sendThread.Start(tbxMessageSend.Text);
  81. }
  82. // 發送消息
  83. private void SendMessage(object obj)
  84. {
  85. string message = obj.ToString();
  86. byte[] messagebytes = Encoding.Unicode.GetBytes(message);
  87. sendUdpClient = new UdpClient();
  88. // 發送消息到組播或廣播地址
  89. sendUdpClient.Send(messagebytes, messagebytes.Length, broadcastIpEndPoint);
  90. sendUdpClient.Close();
  91. // 清空編輯消息框
  92. ResetMessageText(tbxMessageSend);
  93. }
  94. // 利用委托回調機制來實現界面上的消息清空操作
  95. delegate void ResetMessageTextCallBack(TextBox textbox);
  96. private void ResetMessageText(TextBox textbox)
  97. {
  98. if (textbox.InvokeRequired)
  99. {
  100. ResetMessageTextCallBack resetMessageCallback = ResetMessageText;
  101. textbox.Invoke(resetMessageCallback, new object[] { textbox });
  102. }
  103. else
  104. {
  105. textbox.Clear();
  106. textbox.Focus();
  107. }
  108. }
  109. // 接收消息
  110. private void btnReceive_Click(object sender, EventArgs e)
  111. {
  112. chkbxJoinGtoup.Enabled = false;
  113. // 創建接收套接字
  114. IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
  115. IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalport.Text));
  116. receiveUdpClient = new UdpClient(localIpEndPoint);
  117. // 加入組播組
  118. if (chkbxJoinGtoup.Checked == true)
  119. {
  120. receiveUdpClient.JoinMulticastGroup(IPAddress.Parse(tbxGroupIp.Text));
  121. receiveUdpClient.Ttl = 50;
  122. }
  123. // 啟動接受線程
  124. Thread threadReceive = new Thread(ReceiveMessage);
  125. threadReceive.Start();
  126. }
  127. // 接受消息方法
  128. private void ReceiveMessage()
  129. {
  130. IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
  131. while (true)
  132. {
  133. try
  134. {
  135. // 關閉receiveUdpClient時此時會產生異常
  136. byte[] receiveBytes = receiveUdpClient.Receive(ref remoteIpEndPoint);
  137. string receivemessage = Encoding.Unicode.GetString(receiveBytes);
  138. // 顯示消息內容
  139. ShowMessage(lstMessageBox, string.Format("{0}[{1}]", remoteIpEndPoint, receivemessage));
  140. }
  141. catch
  142. {
  143. break;
  144. }
  145. }
  146. }
  147. // 通過委托回調機制顯示消息內容
  148. delegate void ShowMessageCallBack(ListBox listbox,string text);
  149. private void ShowMessage(ListBox listbox, string text)
  150. {
  151. if (listbox.InvokeRequired)
  152. {
  153. ShowMessageCallBack showmessageCallback = ShowMessage;
  154. listbox.Invoke(showmessageCallback, new object[] { listbox, text });
  155. }
  156. else
  157. {
  158. listbox.Items.Add(text);
  159. listbox.SelectedIndex = listbox.Items.Count - 1;
  160. listbox.ClearSelected();
  161. }
  162. }
  163. // 清空消息列表
  164. private void btnClear_Click(object sender, EventArgs e)
  165. {
  166. lstMessageBox.Items.Clear();
  167. }
  168. // 停止接收
  169. private void btnStop_Click(object sender, EventArgs e)
  170. {
  171. chkbxJoinGtoup.Enabled =true;
  172. receiveUdpClient.Close();
  173. }
  174. }
  175. }
技術分享
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace UDPBroadcast
{
    /// <summary>
    /// 在界面上,用戶可以設置本地進程的IP地址和端口號,並將地址加入某個組播組;
    /// 可以輸入發送消息的目的組的地址,並且勾選“廣播”復選框將采用廣播的方式發送信息
    /// 在界面上點擊“接受按鈕”就啟動接收線程,這樣程序就可以接收廣播或組播的信息
    /// </summary>
    public partial class UdpBroadcasefrm : Form
    {
        private UdpClient sendUdpClient;
        private UdpClient receiveUdpClient;
        // 組播IP地址
        IPEndPoint broadcastIpEndPoint;
        public UdpBroadcasefrm()
        {
            InitializeComponent();
            IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
            tbxlocalip.Text = ips[5].ToString();
            tbxlocalport.Text = "8002";
            // 默認組,組播地址是有範圍
            // 具體關於組播和廣播的介紹參照我上一篇博客UDP編程
            // 本地組播組
            tbxGroupIp.Text = "224.0.0.1";
            // 發送到的組播組
            tbxSendToGroupIp.Text = "224.0.0.1";
        }

        // 設置加入組
        private void chkbxJoinGtoup_Click(object sender, EventArgs e)
        {
            if (chkbxJoinGtoup.Checked == true)
            {
                tbxGroupIp.Enabled = false;
            }
            else
            {
                tbxGroupIp.Enabled = true;
                tbxGroupIp.Focus();
            }
        }

        // 選擇發送模式後設置
        private void chkbxBroadcast_Click(object sender, EventArgs e)
        {
            if (chkbxBroadcast.Checked == true)
            {
                tbxSendToGroupIp.Enabled = false;
            }
            else
            {
                tbxSendToGroupIp.Enabled = true;
                tbxSendToGroupIp.Focus();
            }
        }

        // 發送消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (tbxMessageSend.Text == "")
            {
                MessageBox.Show("消息內容不能為空!","提示");
                return;
            }

            // 根據選擇的模式發送信息
            if (chkbxBroadcast.Checked == true)
            {
                // 廣播模式(自動獲得子網中的IP廣播地址)
                broadcastIpEndPoint = new IPEndPoint(IPAddress.Broadcast, 8002);
            }
            else
            {
                // 組播模式
                broadcastIpEndPoint = new IPEndPoint(IPAddress.Parse(tbxSendToGroupIp.Text), 8002);
            }

            // 啟動發送線程發送消息
            Thread sendThread = new Thread(SendMessage);
            sendThread.Start(tbxMessageSend.Text);
        }

        // 發送消息
        private void SendMessage(object obj)
        {
            string message = obj.ToString();
            byte[] messagebytes = Encoding.Unicode.GetBytes(message);
            sendUdpClient = new UdpClient();
            // 發送消息到組播或廣播地址
            sendUdpClient.Send(messagebytes, messagebytes.Length, broadcastIpEndPoint);
            sendUdpClient.Close();
            
            // 清空編輯消息框
            ResetMessageText(tbxMessageSend);
        }

        // 利用委托回調機制來實現界面上的消息清空操作
        delegate void ResetMessageTextCallBack(TextBox textbox);
        private void ResetMessageText(TextBox textbox)
        {
            if (textbox.InvokeRequired)
            {
                ResetMessageTextCallBack resetMessageCallback = ResetMessageText;
                textbox.Invoke(resetMessageCallback, new object[] { textbox });
            }
            else
            {
                textbox.Clear();
                textbox.Focus();
            }
        }

        // 接收消息
        private void btnReceive_Click(object sender, EventArgs e)
        {
            chkbxJoinGtoup.Enabled = false;
            // 創建接收套接字
            IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
            IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalport.Text));
            receiveUdpClient = new UdpClient(localIpEndPoint);
            // 加入組播組
            if (chkbxJoinGtoup.Checked == true)
            {
                receiveUdpClient.JoinMulticastGroup(IPAddress.Parse(tbxGroupIp.Text));
                receiveUdpClient.Ttl = 50;
            }

            // 啟動接受線程
            Thread threadReceive = new Thread(ReceiveMessage);
            threadReceive.Start();
        }

        // 接受消息方法
        private void ReceiveMessage()
        {
            IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
            while (true)
            {
                try
                {
                    // 關閉receiveUdpClient時此時會產生異常
                    byte[] receiveBytes = receiveUdpClient.Receive(ref remoteIpEndPoint);
                    string receivemessage = Encoding.Unicode.GetString(receiveBytes);

                    // 顯示消息內容
                    ShowMessage(lstMessageBox, string.Format("{0}[{1}]", remoteIpEndPoint, receivemessage));
                }
                catch
                {
                    break;
                }
            }
        }
        // 通過委托回調機制顯示消息內容
        delegate void ShowMessageCallBack(ListBox listbox,string text);
        private void ShowMessage(ListBox listbox, string text)
        {
            if (listbox.InvokeRequired)
            {
                ShowMessageCallBack showmessageCallback = ShowMessage;
                listbox.Invoke(showmessageCallback, new object[] { listbox, text });
            }
            else
            {
                listbox.Items.Add(text);
                listbox.SelectedIndex = listbox.Items.Count - 1;
                listbox.ClearSelected();
            }
        }

        // 清空消息列表
        private void btnClear_Click(object sender, EventArgs e)
        {
            lstMessageBox.Items.Clear();
        }

        // 停止接收
        private void btnStop_Click(object sender, EventArgs e)
        {
            chkbxJoinGtoup.Enabled =true;
            receiveUdpClient.Close();
        }

       
    }
}

廣播演示結果(接收端直接點接收按鈕後開啟接受線程,在發送端勾選“廣播選項”輸入發送信息點發送按鈕後的界面如下):

技術分享

下面通過把接收端加入組後的結果,首先終止接收線程,然後勾選“加入組”復選框,然後單擊“接收”按鈕重新開啟接收線程,輸出結果如下:

技術分享

從廣播演示的兩個情況可以看出廣播消息會同時向網上的一切進程轉發,無論這個進程是獨立的還是加入了某個組播組中的進程,都可以接收廣播消息

下面演示下組播的結果:

技術分享

如果把接收端的組地址改為224.0.0.3時,此時發送端發送的消息“組播演示2”將不會發送到不同的組播地址,則接收端就接收不到此時的消息。

從組播結果中可以看出只有加入組播地址224.0.0.2的進程才能接收到信息。

需要註意的地方是:從前面的截圖中可以看出,不論是廣播還是組播,僅僅從收到的信息無從知道發送給它的進程的端口號,所以廣播和組播消息都是匿名發送,並且通過對UDP廣播和組播的理解可以簡單實現一個消息群發的功能(QQ的群裏聊天就是這個原理)。

二、 總結

本專題主要是針對上一專題的補充——實現一個簡單的UDP廣播(組播)程序,通過這樣一個發送端可以發送給在組播地址中的所有用戶和所有子網中的所有用戶。本專題可以說是對UDP編程的一個擴充吧,希望大家看了本專題後可以對UDP協議有大致的理解。在下一個專題中會和大家介紹下P2P編程的相關知識。

全部源碼地址:http://files.cnblogs.com/zhili/UDPBroadcast.zip

[C# 網絡編程系列]專題七:UDP編程補充——UDP廣播程序的實現