[C#] 建立UDP连接、发送广播
说明:
通过建立本地UdpClient与远程UdpClient进行通讯,亦可直接发送到其他已存在的远程端。
基本原理:构建一个本地的udpcSend实例,开启多线程进行监听,然后再发送广播。
案例有字节数组与十六进制字符串相互转换的方法。
一、案例源码
窗体的主要代码:FrmMain.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Net; 8 using System.Net.Sockets; 9 using System.Text; 10 using System.Threading; 11 using System.Threading.Tasks; 12 using System.Windows.Forms; 13 14 namespace NetworkService 15 { 16 public partial class FrmMain : Form 17 { 18 public FrmMain() 19 { 20 InitializeComponent(); 21 22 //string msg = "FF FB 0F"; 23 //byte[] bytes = Encoding.Default.GetBytes(msg); 24 25 26 //string res = ""; 27 //for (int i = 0; i < bytes.Length; i++) 28 //{ 29 // res += bytes[i].ToString(); 30 //} 31 //MessageBox.Show(res); 32 33 34 } 35 36 //本地收发UDPClient实例 37 private UdpClient udpcSend; 38 private UdpClient udpcRecv; 39 private bool isLocalConneted = false;//本地是否已连接 40 private Thread thrRecv;//本地监听接收线程 41 42 /// <summary> 43 /// 【构建本地连接】 44 /// 20181021 45 /// </summary> 46 /// <param name="sender"></param> 47 /// <param name="e"></param> 48 private void btnConnect_Click(object sender, EventArgs e) 49 { 50 if (cbProtocol.SelectedIndex != 0) 51 { 52 MessageBox.Show("请暂时选择UDP协议!", "系统提示"); 53 return; 54 } 55 56 if (string.IsNullOrWhiteSpace(tbLocalIP.Text) || string.IsNullOrWhiteSpace(tbLocalPort.Text)) 57 { 58 MessageBox.Show("请填写本地IP和端口", "系统提示"); 59 return; 60 } 61 62 63 if (!isLocalConneted)//构建本地连接 64 { 65 IPEndPoint localIpep = new IPEndPoint(IPAddress.Parse(tbLocalIP.Text), Convert.ToInt32(tbLocalPort.Text)); 66 udpcSend = new UdpClient(localIpep); 67 68 btnConnect.Text = "断开"; 69 isLocalConneted = true; 70 71 //启动监听接收信息 72 BeginListenRecv(); 73 } 74 else//断开本地连接 75 { 76 //关闭监听线程 77 thrRecv.Abort(); 78 udpcRecv.Close(); 79 80 udpcSend.Close(); 81 btnConnect.Text = "连接"; 82 isLocalConneted = false; 83 84 85 ShowMessage(tbRecvMsg, "[OS]:UDP监听已成功关闭"); 86 } 87 } 88 89 90 91 /// <summary> 92 /// 【开始监听】 93 /// 20181021 94 /// </summary> 95 private void BeginListenRecv() 96 { 97 udpcRecv = udpcSend;//接收与发送使用同一个实例 98 99 thrRecv = new Thread(ReceiveMessage); 100 thrRecv.Start(); 101 102 ShowMessage(tbRecvMsg, "[OS]:Local UDP 监听已成功启动"); 103 104 } 105 106 /// <summary> 107 /// 【接收信息】 108 /// 20181021 109 /// </summary> 110 private void ReceiveMessage() 111 { 112 IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);//接收任何来源的信息 113 while (true) 114 { 115 byte[] byteRecv = udpcRecv.Receive(ref remoteIpep); 116 string message = ByteArrayToHexStringNoBlank(byteRecv); 117 118 //remoteIpep.Address.ToString() 获取发送源的IP 119 ShowMessage(tbRecvMsg, "[Local Recv]:" + remoteIpep.Address + ":" + remoteIpep.Port + "=" + message); 120 121 } 122 123 } 124 125 /// <summary> 126 /// 【发送消息】 127 /// 20181021 128 /// </summary> 129 /// <param name="obj"></param> 130 private void SendMessage(object obj) 131 { 132 string[] data = (string[])obj; 133 134 //字符串转16进制 135 byte[] sendBytes = HexStringToByteArray(data[0]); 136 137 //发送到远程IP和端口 138 IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse(data[1]), Convert.ToInt32(data[2])); 139 140 udpcSend.Send(sendBytes, sendBytes.Length, remoteIpep); 141 142 143 ShowMessage(tbRecvMsg, "[Local Send]:" + data[0]); 144 ResetTextBox(tbSendMsg); 145 } 146 147 148 //构建远程服务的收发实例 149 private UdpClient remoteUdpcSend; 150 private UdpClient remoteUdpcRecv; 151 private Thread remoteThrRecv; 152 153 private Thread thrSend; 154 private bool isRemoteCreated = false; 155 /// <summary> 156 /// 【发送消息】 157 /// 20181021 158 /// </summary> 159 /// <param name="sender"></param> 160 /// <param name="e"></param> 161 private void btnSend_Click(object sender, EventArgs e) 162 { 163 if (string.IsNullOrWhiteSpace(tbRemoteIP.Text) || string.IsNullOrWhiteSpace(tbRemotePort.Text)) 164 { 165 MessageBox.Show("远程IP和端口不能为空!", "系统提示"); 166 return; 167 } 168 169 if (string.IsNullOrWhiteSpace(tbSendMsg.Text)) 170 { 171 MessageBox.Show("发送的消息不能为空!", "系统提示"); 172 return; 173 } 174 175 if (!isLocalConneted) 176 { 177 MessageBox.Show("请先建立本地连接!", "系统提示"); 178 return; 179 } 180 181 182 //构建本地远程 183 if (cbIsRemoteAsLocal.Checked)//构建本地的远程服务,本地处理 184 { 185 if (!isRemoteCreated) 186 { 187 IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse(tbRemoteIP.Text), Convert.ToInt32(tbRemotePort.Text));//构建远程端口 188 remoteUdpcRecv = new UdpClient(remoteIpep); 189 190 //启动远程的监听 191 remoteThrRecv = new Thread(RemoteRecvMessage); 192 remoteThrRecv.Start(); 193 194 ShowMessage(tbRecvMsg, "[OS]:Remote UDP 监听启动完成!"); 195 isRemoteCreated = true;//远程服务已启动 196 } 197 else 198 { 199 //发送消息 200 } 201 202 } 203 else 204 { 205 //发送消息, 206 } 207 208 //发送远程,这里不做处理,直接调用udpcSend发送即可 209 thrSend = new Thread(new ParameterizedThreadStart(SendMessage)); 210 211 string[] param = new string[] { tbSendMsg.Text, tbRemoteIP.Text, tbRemotePort.Text }; 212 thrSend.Start(param); 213 214 } 215 216 /// <summary> 217 /// 【本地远程发送消息】 218 /// 20181021 219 /// </summary> 220 /// <param name="remoteIpep"></param> 221 private void RemoteSendMessage(IPEndPoint remoteIpep) 222 { 223 remoteUdpcSend = remoteUdpcRecv; 224 225 byte[] msg = Encoding.Default.GetBytes("远程已收到你的消息"); 226 227 remoteUdpcSend.Send(msg, msg.Length, remoteIpep); 228 229 ShowMessage(tbRecvMsg, "[Remote Send]:" + "远程已收到你的消息"); 230 } 231 232 /// <summary> 233 /// 【本地远程接收信息】 234 /// 20181021 235 /// </summary> 236 private void RemoteRecvMessage() 237 { 238 IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);//接收任何来源的信息 239 while (true) 240 { 241 byte[] byteRemoteRecv = remoteUdpcRecv.Receive(ref remoteIpep); 242 string message = Encoding.Default.GetString(byteRemoteRecv); 243 244 //接收到信息 245 if (byteRemoteRecv.Length > 0) 246 { 247 //remoteIpep.Address.ToString() 获取发送源的IP 248 ShowMessage(tbRecvMsg, "[Remote Recv]:" + remoteIpep.Address + ":" + remoteIpep.Port + "=" + message); 249 250 RemoteSendMessage(remoteIpep); 251 } 252 253 } 254 } 255 256 //更新显示TextBox 257 delegate void ShowMessageDelegate(TextBox tb, string msg); 258 private void ShowMessage(TextBox tb, string msg) 259 { 260 if (tb.InvokeRequired) 261 { 262 ShowMessageDelegate showMessageDelegate = ShowMessage; 263 tb.Invoke(showMessageDelegate, new object[] { tb, msg }); 264 } 265 else 266 { 267 tb.Text += msg + "\r\n"; 268 } 269 } 270 271 //清空TextBox 272 delegate void ResetTextBoxDelegate(TextBox tb); 273 private void ResetTextBox(TextBox tb) 274 { 275 if (tb.InvokeRequired) 276 { 277 ResetTextBoxDelegate resetTextBoxDelegate = ResetTextBox; 278 tb.Invoke(resetTextBoxDelegate, new object[] { tb }); 279 } 280 else 281 { 282 tb.Text = ""; 283 } 284 } 285 286 /// <summary> 287 /// 【字符串转16进制】 288 /// 20181021 289 /// </summary> 290 /// <param name="s"></param> 291 /// <returns></returns> 292 public byte[] HexStringToByteArray(string s) 293 { 294 if (s.Length == 0) 295 throw new Exception("字符串长度为0"); 296 s = s.Replace(" ", ""); 297 byte[] buffer = new byte[s.Length / 2]; 298 for (int i = 0; i < s.Length; i += 2) 299 buffer[i / 2] = Convert.ToByte(s.Substring(i, 2), 16); 300 return buffer; 301 } 302 303 /// <summary> 304 /// 【字节数组转换成十六进制字符串(不带空格)】 305 /// 20181021 306 /// </summary> 307 /// <param name="data">要转换的字节数组</param> 308 /// <returns>转换后的字符串</returns> 309 public string ByteArrayToHexStringNoBlank(byte[] data) 310 { 311 StringBuilder sb = new StringBuilder(data.Length * 3); 312 foreach (byte b in data) 313 sb.Append(Convert.ToString(b, 16).PadLeft(2, '0') + " "); 314 return sb.ToString().ToUpper(); 315 } 316 317 /// <summary> 318 /// 【窗体退出,关闭子线程】 319 /// 20181021 320 /// </summary> 321 /// <param name="sender"></param> 322 /// <param name="e"></param> 323 private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) 324 { 325 Environment.Exit(0); 326 } 327 328 329 330 331 } 332 }
二、界面
生命不息,学习不止