专题七:UDP编程补充——UDP广播程序的实现

一、程序实现

UDP广播程序的实现代码:

  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 
  8 namespace UDPBroadcast
  9 {
 10     /// <summary>
 11     /// 在界面上,用户可以设置本地进程的IP地址和端口号,并将地址加入某个组播组;
 12     /// 可以输入发送消息的目的组的地址,并且勾选“广播”复选框将采用广播的方式发送信息
 13     /// 在界面上点击“接受按钮”就启动接收线程,这样程序就可以接收广播或组播的信息
 14     /// </summary>
 15     public partial class UdpBroadcasefrm : Form
 16     {
 17         private UdpClient sendUdpClient;
 18         private UdpClient receiveUdpClient;
 19         // 组播IP地址
 20         IPEndPoint broadcastIpEndPoint;
 21         public UdpBroadcasefrm()
 22         {
 23             InitializeComponent();
 24             IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
 25             tbxlocalip.Text = ips[5].ToString();
 26             tbxlocalport.Text = "8002";
 27             // 默认组,组播地址是有范围
 28             // 具体关于组播和广播的介绍参照我上一篇博客UDP编程
 29             // 本地组播组
 30             tbxGroupIp.Text = "224.0.0.1";
 31             // 发送到的组播组
 32             tbxSendToGroupIp.Text = "224.0.0.1";
 33         }
 34 
 35         // 设置加入组
 36         private void chkbxJoinGtoup_Click(object sender, EventArgs e)
 37         {
 38             if (chkbxJoinGtoup.Checked == true)
 39             {
 40                 tbxGroupIp.Enabled = false;
 41             }
 42             else
 43             {
 44                 tbxGroupIp.Enabled = true;
 45                 tbxGroupIp.Focus();
 46             }
 47         }
 48 
 49         // 选择发送模式后设置
 50         private void chkbxBroadcast_Click(object sender, EventArgs e)
 51         {
 52             if (chkbxBroadcast.Checked == true)
 53             {
 54                 tbxSendToGroupIp.Enabled = false;
 55             }
 56             else
 57             {
 58                 tbxSendToGroupIp.Enabled = true;
 59                 tbxSendToGroupIp.Focus();
 60             }
 61         }
 62 
 63         // 发送消息
 64         private void btnSend_Click(object sender, EventArgs e)
 65         {
 66             if (tbxMessageSend.Text == "")
 67             {
 68                 MessageBox.Show("消息内容不能为空!","提示");
 69                 return;
 70             }
 71 
 72             // 根据选择的模式发送信息
 73             if (chkbxBroadcast.Checked == true)
 74             {
 75                 // 广播模式(自动获得子网中的IP广播地址)
 76                 broadcastIpEndPoint = new IPEndPoint(IPAddress.Broadcast, 8002);
 77             }
 78             else
 79             {
 80                 // 组播模式
 81                 broadcastIpEndPoint = new IPEndPoint(IPAddress.Parse(tbxSendToGroupIp.Text), 8002);
 82             }
 83 
 84             // 启动发送线程发送消息
 85             Thread sendThread = new Thread(SendMessage);
 86             sendThread.Start(tbxMessageSend.Text);
 87         }
 88 
 89         // 发送消息
 90         private void SendMessage(object obj)
 91         {
 92             string message = obj.ToString();
 93             byte[] messagebytes = Encoding.Unicode.GetBytes(message);
 94             sendUdpClient = new UdpClient();
 95             // 发送消息到组播或广播地址
 96             sendUdpClient.Send(messagebytes, messagebytes.Length, broadcastIpEndPoint);
 97             sendUdpClient.Close();
 98             
 99             // 清空编辑消息框
100             ResetMessageText(tbxMessageSend);
101         }
102 
103         // 利用委托回调机制来实现界面上的消息清空操作
104         delegate void ResetMessageTextCallBack(TextBox textbox);
105         private void ResetMessageText(TextBox textbox)
106         {
107             if (textbox.InvokeRequired)
108             {
109                 ResetMessageTextCallBack resetMessageCallback = ResetMessageText;
110                 textbox.Invoke(resetMessageCallback, new object[] { textbox });
111             }
112             else
113             {
114                 textbox.Clear();
115                 textbox.Focus();
116             }
117         }
118 
119         // 接收消息
120         private void btnReceive_Click(object sender, EventArgs e)
121         {
122             chkbxJoinGtoup.Enabled = false;
123             // 创建接收套接字
124             IPAddress localIp = IPAddress.Parse(tbxlocalip.Text);
125             IPEndPoint localIpEndPoint = new IPEndPoint(localIp, int.Parse(tbxlocalport.Text));
126             receiveUdpClient = new UdpClient(localIpEndPoint);
127             // 加入组播组
128             if (chkbxJoinGtoup.Checked == true)
129             {
130                 receiveUdpClient.JoinMulticastGroup(IPAddress.Parse(tbxGroupIp.Text));
131                 receiveUdpClient.Ttl = 50;
132             }
133 
134             // 启动接受线程
135             Thread threadReceive = new Thread(ReceiveMessage);
136             threadReceive.Start();
137         }
138 
139         // 接受消息方法
140         private void ReceiveMessage()
141         {
142             IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
143             while (true)
144             {
145                 try
146                 {
147                     // 关闭receiveUdpClient时此时会产生异常
148                     byte[] receiveBytes = receiveUdpClient.Receive(ref remoteIpEndPoint);
149                     string receivemessage = Encoding.Unicode.GetString(receiveBytes);
150 
151                     // 显示消息内容
152                     ShowMessage(lstMessageBox, string.Format("{0}[{1}]", remoteIpEndPoint, receivemessage));
153                 }
154                 catch
155                 {
156                     break;
157                 }
158             }
159         }
160         // 通过委托回调机制显示消息内容
161         delegate void ShowMessageCallBack(ListBox listbox,string text);
162         private void ShowMessage(ListBox listbox, string text)
163         {
164             if (listbox.InvokeRequired)
165             {
166                 ShowMessageCallBack showmessageCallback = ShowMessage;
167                 listbox.Invoke(showmessageCallback, new object[] { listbox, text });
168             }
169             else
170             {
171                 listbox.Items.Add(text);
172                 listbox.SelectedIndex = listbox.Items.Count - 1;
173                 listbox.ClearSelected();
174             }
175         }
176 
177         // 清空消息列表
178         private void btnClear_Click(object sender, EventArgs e)
179         {
180             lstMessageBox.Items.Clear();
181         }
182 
183         // 停止接收
184         private void btnStop_Click(object sender, EventArgs e)
185         {
186             chkbxJoinGtoup.Enabled =true;
187             receiveUdpClient.Close();
188         }
189 
190        
191     }
192 }

广播演示结果(接收端直接点接收按钮后开启接受线程,在发送端勾选“广播选项”输入发送信息点发送按钮后的界面如下):

下面通过把接收端加入组后的结果,首先终止接收线程,然后勾选“加入组”复选框,然后单击“接收”按钮重新开启接收线程,输出结果如下:

从广播演示的两个情况可以看出广播消息会同时向网上的一切进程转发,无论这个进程是独立的还是加入了某个组播组中的进程,都可以接收广播消息

下面演示下组播的结果:

如果把接收端的组地址改为224.0.0.3时,此时发送端发送的消息“组播演示2”将不会发送到不同的组播地址,则接收端就接收不到此时的消息。

从组播结果中可以看出只有加入组播地址224.0.0.2的进程才能接收到信息。

需要注意的地方是:从前面的截图中可以看出,不论是广播还是组播,仅仅从收到的信息无从知道发送给它的进程的端口号,所以广播和组播消息都是匿名发送,并且通过对UDP广播和组播的理解可以简单实现一个消息群发的功能(QQ的群里聊天就是这个原理)。

二、 总结

本专题主要是针对上一专题的补充——实现一个简单的UDP广播(组播)程序,通过这样一个发送端可以发送给在组播地址中的所有用户和所有子网中的所有用户。本专题可以说是对UDP编程的一个扩充吧,希望大家看了本专题后可以对UDP协议有大致的理解。在下一个专题中会和大家介绍下P2P编程的相关知识。

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

原文地址:https://www.cnblogs.com/wmcoder/p/5216246.html