c#UDP发送接收消息
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传输文件。