其实udp不存在客户端和服务端之分。我这里主要区分两个程序。因为有个程序需要指定ip.所以我就就把它叫客户端。客户端会先指定ip。并且异步方法接收消息。一旦受到消息,会交给回调函数处理。
服务端会保存客户端发来的ip和端口。用于回发。服务端也实现异步接收消息的方法。
并且消息显示方面。我用了ManualReSetevent来同步控制窗体的显示。注释写的很清楚。本人还是学生,可能注释理解有错,希望各位指点。
我觉得程序最大的特点就是界面不会卡顿,而且一旦设置好ip,客户端和服务端就能随意发送和收取消息。
我画了个图:左右两边的四个小箭头我主要想说明循环调用;
客户端代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; namespace CHEINGDDDDDDD { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public static Socket udpReceive;//接收对象 public static Socket udpSend;//发送对象 public const int udpR_PROT = 9095; public const int udpS_PORT = 9096; public static byte[] udpReDataBuf;//接收字节数组 public static byte[] udpSeDataBuf;//发送字节数组 public static bool setIP = false;//用于判断ip是否正确 public static EndPoint remoteEP; public static IPEndPoint remoteIPEP; public static IPAddress remoteIpadress; public static int sendLen; public static string messageC;//收到消息的字符窜 public static string messageS;//发送消息的字符窜 public static int receiveLen;//收到消息的的长度 public static IntPtr Main_wnd_handle;//窗体句柄 public static ManualResetEvent close;//同步控制线程结束 public static ManualResetEvent come;//同步控制线程消息收 public static ManualResetEvent post;//同步控制线程消息发 public static ManualResetEvent[] list; public const int MESSAGE_SEND = 0x521;//消息常量 发送 public const int MESSAGE_CON = 0x520;//消息常量 接受 [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage( IntPtr hWnd, int Msg, int wParam, int lParam ); public static void Setdate()//用于数据初始化 { come = new ManualResetEvent(false); close = new ManualResetEvent(false); post = new ManualResetEvent(false); list = new ManualResetEvent[3]; list[0] = close;//0 线程结束 list[1] = come;//1 消息到达 list[2] = post;//2 消息发送 //初始消息为1k udpReDataBuf = new byte[1024]; udpSeDataBuf = new byte[1024]; //初始发送的ip地址和端口的封装,会在后面的发送按钮明确ip和端口 remoteIPEP = new IPEndPoint(IPAddress.Any, 0); //用于接收的Socket的绑定,确定的端口 IPEndPoint iep = new IPEndPoint(IPAddress.Any, udpR_PROT); //new Socket收 udpReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //绑定 udpReceive.Bind(iep); //注意remoteEP是EndPoint型,是抽象类,所以不能new remoteEP = (EndPoint)remoteIPEP; //BeginReceiveFrom是异步的方法接收消息,注意ref remote ,当有消息来会写入ip地址 udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object()); //new Socket 发 udpSend = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //工作线程启动 ThreadStart workStart = new ThreadStart(doing); Thread gogo = new Thread(workStart); gogo.Start(); } private void Form1_Load(object sender, EventArgs e) { //句柄赋值,初始化函数的运行和线程的启动 Main_wnd_handle = this.Handle; Setdate(); } //窗体消息回调函数。用于同步显示发送消息。 //消息来就添加到texbox2;消息发送就清空texbox3,同时显示在texbox2 protected override void DefWndProc(ref Message m) { switch (m.Msg) { case MESSAGE_CON: textBox2.AppendText("服务器:" + messageC+"\n"); break; case MESSAGE_SEND: textBox3.Clear(); textBox2.AppendText("客户机:" + messageS + "\n"); break; default: base.DefWndProc(ref m); break; } } //消息发送按钮 private void button1_Click(object sender, EventArgs e) { remoteIPEP.Address = remoteIpadress;//远程ip remoteIPEP.Port = udpS_PORT;//远程端口 messageS = textBox3.Text;//发送的消息字符串 udpSeDataBuf = Encoding.UTF8.GetBytes(messageS);//字节窜封装成字节数组 udpSend.SendTo(udpSeDataBuf, remoteIPEP);//发送 post.Set();//同步控制窗体显示 } //接收消息的回调函数 public static void ReceiveServerCallback(IAsyncResult ar) { try { //远程ip的写入,字节流转换成字符窜 EndPoint tmpremoteEP = (EndPoint)remoteIPEP; receiveLen = udpReceive.EndReceiveFrom(ar, ref tmpremoteEP); messageC = Encoding.UTF8.GetString(udpReDataBuf, 0, receiveLen); int len = receiveLen; //有消息来同步控制窗体显示 if (len != 0) { come.Set(); } //再次调用异步方法接收 udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object()); } catch (SocketException se) { MessageBox.Show(se.Message); } } //工作线程控制窗体显示 public static void doing() { int select; bool working = true; while (working) { select = WaitHandle.WaitAny(list, 1000); switch (select) { case 0: working = false; break; case 1: come.Reset(); SendMessage(Main_wnd_handle, MESSAGE_CON, 0, 0); //消息到达 break; case 2: post.Reset(); SendMessage(Main_wnd_handle, MESSAGE_SEND, 0, 0); //消息发送 break; case WaitHandle.WaitTimeout: //超时处理 break; } } } //判断写入的ip是否正确 private void button2_Click(object sender, EventArgs e) { setIP = IPAddress.TryParse(textBox1.Text, out remoteIpadress); if (!setIP) { label1.Text = "输入IP不正确"; textBox1.Focus(); } else { label1.Text = "IP地址解析正确"; } } //结束工作线程 private void button3_Click(object sender, EventArgs e) { close.Set(); } } }
服务端代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Main_wnd_handle = this.Handle; Setdate(); } //设置变量 public const int MESSAGE_CON = 0x520;//消息常亮 收 public const int MESSAGE_SEND = 0x521;//消息常量 发 public static IntPtr Main_wnd_handle;//窗体句柄 public static Socket udpReceive;//socket 收 public static Socket udpSend;//socket 发 public const int udpR_PORT = 9096; public const int udpS_PORT = 9095; public static IPEndPoint remoteIPEP; public static EndPoint remoteEP; public static byte[] udpSeDataBuf; public static byte[] udpReDataBuf; public static int receiveLen; public static string messageC; public static string messageS; public static ManualResetEvent post; public static ManualResetEvent close; public static ManualResetEvent come; public static ManualResetEvent[] list; [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage( IntPtr hWnd, int Msg, int wParam, int lParam ); //窗体回调处理 protected override void DefWndProc(ref Message m) { switch (m.Msg) { case MESSAGE_CON: textBox1.AppendText("客户机:" + messageC+"\n"); break; case MESSAGE_SEND: textBox2.Clear(); textBox1.AppendText("服务器:" + messageS + "\n"); break; default: base.DefWndProc(ref m); break; } } public static void Setdate() { come = new ManualResetEvent(false); post = new ManualResetEvent(false); close = new ManualResetEvent(false); list = new ManualResetEvent[3]; list[0] = close; list[1] = come; list[2] = post; udpReDataBuf = new byte[1024]; udpSeDataBuf = new byte[1024]; remoteIPEP = new IPEndPoint(IPAddress.Any, 0); IPEndPoint iep = new IPEndPoint(IPAddress.Any, udpR_PORT); udpReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpReceive.Bind(iep); udpSend = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); remoteEP = (EndPoint)remoteIPEP; udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object()); ThreadStart workStart = new ThreadStart(doing); Thread gogo = new Thread(workStart); gogo.Start(); } public static void doing() { bool linlin = true; int select; while (linlin) { select = WaitHandle.WaitAny(list, 1000); switch (select) { case 0: linlin = false; break; case 1: come.Reset(); SendMessage(Main_wnd_handle, MESSAGE_CON, 0, 0); break; case 2: post.Reset(); SendMessage(Main_wnd_handle, MESSAGE_SEND, 0, 0); break; case WaitHandle.WaitTimeout: break; } } } public static void ReceiveServerCallback(IAsyncResult ar) { try { //实际写入远程ip的地方,所以需要个临时tmpremoteEP写入。再装换成IPEndPoint类型给remoteIPEP,用于确定会发的ip地址 EndPoint tmpremoteEP = (EndPoint)remoteIPEP; receiveLen = udpReceive.EndReceiveFrom(ar, ref tmpremoteEP); remoteIPEP = (IPEndPoint)tmpremoteEP; messageC = Encoding.UTF8.GetString(udpReDataBuf, 0, receiveLen); if (receiveLen != 0) { come.Set(); } udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object()); } catch (SocketException se) { MessageBox.Show(se.Message); } } private void button1_Click(object sender, EventArgs e) { messageS = textBox2.Text; udpSeDataBuf = Encoding.UTF8.GetBytes(messageS); //注意远程端口的绑定 remoteIPEP.Port = udpS_PORT; udpSend.SendTo(udpSeDataBuf, remoteIPEP); post.Set(); } } }
我画了个图:左右两边的四个小箭头我主要想说明循环调用;
时间: 2024-10-25 09:20:46