专题七: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编程的相关知识。