[C# 网络编程系列]专题七:UDP编程补充——UDP广播程序的实现
上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享下,本专题主要介绍下如何实现UDP广播的程序,下面就直接介绍实现过程和代码以及运行的结果。
一、程序实现
UDP广播程序的实现代码:
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编程的相关知识。