UDP作為一種簡單的、面向數據報的無連接的協議,雖然提供的是不可靠的服務,但是從速度上、一對多傳輸方面比TCP有着很大的優勢。本文主要講解UDP信息的發送和接收。
Demo界面圖如下:
首先打開程序exe文件開啟“接收”的服務,然后再次啟動程序,輸入信息,即可發送信息了,效果圖如下:
細心的人會發現,我在接受消息時,已經把接收到的每一個字符的ASCII碼的十進制值給打印出來了,這是為了區別Encoding.Default和Encoding.Unicode編碼方式的區別。
如下面的小例子:
用Encoding.Default方式進行編碼
string message = "hello"; byte[] sendbytes = Encoding.Default.GetBytes(message); for (int i = 0; i < sendbytes.Length; i++) { ShwMsgForView.ShwMsgforView(listBox, string.Format("{0}", sendbytes[i].ToString())); } ShwMsgForView.ShwMsgforView(listBox, "發送消息:" + message); return;
輸出的結果為:
用Encoding.Unicode方式進行編碼
string message = "hello"; //byte[] sendbytes = Encoding.Default.GetBytes(message); byte[] sendbytes = Encoding.Unicode.GetBytes(message); for (int i = 0; i < sendbytes.Length; i++) { ShwMsgForView.ShwMsgforView(listBox, string.Format("{0}", sendbytes[i].ToString())); } ShwMsgForView.ShwMsgforView(listBox, "發送消息:" + message); return;
輸出的結果為:
結果一目了然,如果使用Encoding.Unicode編碼方式對字符串進行編碼的話,會自動的在每一個字符后面加一個0,這是因為在字符串轉換到字節數組的過程中,Encoding.Default 會將每個單字節字符,
如半角英文,轉換成一個字節;而把每個雙字節字符,如漢字,轉換成2個字節。而 Encoding.Unicode 則會將它們都轉換成兩個字節。因為和我們進行數據通信的終端基本都會傳輸ASCII碼值,而不會進行Unicode編碼
所以,我采用的Default編碼方式。
下面為系統代碼:
public Main() { InitializeComponent(); systemLog = new BusinessLogicLayer.SystemLog(); IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName()); txtlocalIp.Text = ips[1].ToString(); int port = 51888; txtlocalPort.Text = port.ToString(); txtSndtoIP.Text = ips[1].ToString(); txtSndtoPort.Text = port.ToString(); chkbxAnonymous.Checked = true; btnStop.Enabled = false; }
這里我在獲取本機IP地址時用的是ips[1].ToString(),發現很多都是用ips[0].ToString()。經過調試發現,ips[0].ToString()為IPV6的地址,而ips[1].ToString()才是IPV4的地址。如下圖:
調試圖
CMD查看ipconfig圖
發送信息類代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; using System.Windows.Forms; namespace BusinessLogicLayer { public class SendMessage { private static UdpClient sendUdpClient; private static string sendIP; private static string sendToPort; private static ListBox listBox; private static SystemLog systemLog = new SystemLog(); #region 發送消息 /// <summary> /// 發送消息 /// </summary> /// <param name="sendMsg"></param> /// <param name="sendIp"></param> /// <param name="sendPort"></param> public static void SendMsgStart(string sendMsg, string sendIp, string sendPort, ListBox listbox) { //給參數賦值 sendIP = sendIp; sendToPort = sendPort; listBox = listbox; //選擇發送模式 //固定為匿名模式(套接字綁定的端口由系統自動分配) sendUdpClient = new UdpClient(0); //啟動發送線程 Thread threadSend = new Thread(SendMessages); threadSend.IsBackground = true; threadSend.Start(sendMsg); //顯示狀態 ShwMsgForView.ShwMsgforView(listBox, "發送線程啟動"); //將數據存入數據庫 systemLog.SaveSystemLog("", "發送線程啟動", "管理員"); } private static void SendMessages(object obj) { string message = (string)obj; //byte[] sendbytes = Encoding.Unicode.GetBytes(message); //使用Default編碼,如果使用Unicode編碼的話,每個字符中間都會有個0,影響解碼 byte[] sendbytes = Encoding.Default.GetBytes(message); IPAddress remoteIp = IPAddress.Parse(sendIP); IPEndPoint remoteIPEndPoint = new IPEndPoint(remoteIp, int.Parse(sendToPort)); sendUdpClient.Send(sendbytes, sendbytes.Length, remoteIPEndPoint); sendUdpClient.Close(); ShwMsgForView.ShwMsgforView(listBox, "發送消息:" + message); systemLog.SaveSystemLog("", "發送消息,目標:" + remoteIPEndPoint + ",消息內容為:" + message + "", "管理員"); } #endregion } }
接受消息類代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; using System.Windows.Forms; namespace BusinessLogicLayer { public class ReceiveMessage { private static UdpClient receiveUdpClient; private static ListBox listBox; private static SystemLog systemLog = new SystemLog(); #region 接受消息 public static void ReceiveStart(string localip,string localPort,ListBox listbox) { listBox = listbox; //創建接受套接字 IPAddress localIP = IPAddress.Parse(localip); IPEndPoint localIPEndPoint = new IPEndPoint(localIP, int.Parse(localPort)); receiveUdpClient = new UdpClient(localIPEndPoint); //啟動接受線程 Thread threadReceive = new Thread(ReceiveMessages); threadReceive.IsBackground = true; threadReceive.Start(); //顯示狀態 ShwMsgForView.ShwMsgforView(listBox, "接受線程啟動"); //將數據存入數據庫 systemLog.SaveSystemLog("", "接受線程啟動", "管理員"); } private static void ReceiveMessages() { IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, 0); while (true) { try { //關閉receiveUdpClient時此句會產生異常 byte[] receiveBytes = receiveUdpClient.Receive(ref remoteIPEndPoint); for (int i = 0; i < receiveBytes.Length; i++) { ShwMsgForView.ShwMsgforView(listBox, string.Format("{0}[{1}]", remoteIPEndPoint, receiveBytes[i].ToString())); } // string message = Encoding.Unicode.GetString(receiveBytes, 0, receiveBytes.Length); string message = Encoding.ASCII.GetString(receiveBytes, 0, receiveBytes.Length); //顯示接受到的消息內容 ShwMsgForView.ShwMsgforView(listBox, string.Format("{0}[{1}]", remoteIPEndPoint, message)); } catch { break; } } } public static void CloseReceiveUdpClient() { receiveUdpClient.Close(); ShwMsgForView.ShwMsgforView(listBox, "接收線程停止"); systemLog.SaveSystemLog("", "接收線程停止", "管理員"); } #endregion } }
顯示消息類代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace BusinessLogicLayer { public class ShwMsgForView { delegate void ShwMsgforViewCallBack(ListBox listbox, string text); public static void ShwMsgforView(ListBox listbox, string text) { if (listbox.InvokeRequired) { ShwMsgforViewCallBack shwMsgforViewCallBack = ShwMsgforView; listbox.Invoke(shwMsgforViewCallBack, new object[] { listbox, text }); } else { listbox.Items.Add(text); listbox.SelectedIndex = listbox.Items.Count - 1; listbox.ClearSelected(); } } } }
至此UDP發送和接收消息搞定,下篇文章分享下使用TCP傳輸文件。