Socket通信
用Socket通信实现本地计算机与云服务器的连接,云服务器端有一个简单的增量式PID算法的计算代码。本地依次输入速度设定值并通过Socket通信传送到服务器端,服务器端将PID计算得到的实际速度值存放在一个集合中,然后再次建立新的套接字,与另一个客户端建立通信连接,实现将PID计算得到的实际速度值传送到新的客户端中。
客户端-1代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections; namespace _3_4客户端_PID { public class Program { static void Main(string[] args) { byte[] data = new byte[1024]; string strInput; string stringData; //创建服务器端IPEndPoint对象 IPEndPoint Ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 125); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { server.Connect(Ipep);//Connect 建立与远程主机的连接 } catch (Exception e) { Console.WriteLine("无法连接到服务器"); Console.WriteLine(e.ToString()); return; } finally { } //接收数据 int receive = server.Receive(data);//receive是接收到的数据的长度 所以是int型 接收到的信息,存在data这个byte字节数组中 stringData = Encoding.ASCII.GetString(data, 0, receive); Console.WriteLine(stringData);//将从服务器接收到的数据打印出来 while (true) { Console.WriteLine("请输入速度设定值,输入esc断开与服务器的连接,程序退出"); strInput = Console.ReadLine(); if (strInput == "esc") { break;//break,跳出循环,所以不会执行后边的send语句,本次发送到服务器端的数据长度为0 } //发送数据 try { double douInput = Convert.ToDouble(strInput); server.Send(Encoding.ASCII.GetBytes(strInput)); } catch { Console.WriteLine("输入的设定速度格式不正确,请重新输入"); Console.ReadKey(); } } //释放资源 Console.WriteLine("断开与服务器的连接"); server.Shutdown(SocketShutdown.Both); server.Close(); Console.ReadKey(); } } }
服务器端代码:
注意,127.0.0.1是本机循环地址 若想建立与非本机计算机的通信连接,要把IP地址改写为服务器端所在计算机的ip地址 云服务器就写公网IP即可。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections; using System.Threading; using System.Diagnostics; namespace _3_3_服务器端_PID { public static class actualSpeed { public static ArrayList list = new ArrayList();//集合存储PID控制器计算后的输出值 public static double number; } public class Program { static void Main(string[] args) { double[] nums = new double[3];//存储3个偏差值e(k) e(k-1) e(k-2) double actual = 0; double set = 0; //int receive;//接收到的数据长度 //定义一个空字节数组date作为数据缓冲区,用于缓冲流入和流出的信息 byte[] date = new byte[1024]; //为本服务器定义IPEndPoint对象 IPEndPoint Ipep = new IPEndPoint(IPAddress.Any, 125);//指定地址和端口号 Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock.Bind(Ipep); newsock.Listen(10);//置于监听状态 Console.WriteLine("waiting for a message from client"); //接收来自客户端的接入尝试连接,并返回连接客户端的ip地址 Socket client = newsock.Accept(); IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; //返回客户端的ip地址和端口号 Console.WriteLine("connected with {0} at port {1}", clientep.Address, clientep.Port); //发送欢迎消息到客户端,等待客户端消息 string welcome = "welcome to the server"; date = Encoding.ASCII.GetBytes(welcome);//以字节数组的形式 将欢迎信息发送给客户端 client.Send(date, date.Length, SocketFlags.None); // Send/Receive() 进行数据传送 Send发送数据 int i = 1;//作为下标,给静态数组actualSpeed依次赋值 while (true) { //定义缓冲区 date = new byte[1024]; int recei = client.Receive(date);//Receive接收数据 返回接收数据的长度 所以为int类型 //如果接收到的数据长度是0,则自动退出本次while循环 if (recei == 0) { break; } string strSet = Encoding.ASCII.GetString(date, 0, recei); set = double.Parse(strSet); nums[0] = set - actual; double increase = 0.4 * (nums[0] - nums[1]) + 0.53 * nums[0] + 0.1 * (nums[0] - 2 * nums[1] + nums[2]); actual += increase; actualSpeed.list.Add(actual);//将每次的实际速度值 添加到集合中 Console.WriteLine(actualSpeed.list[i - 1]);//输出实际的速度值 成功! i++; Console.WriteLine("设定速度为{0},增长的速度为{1},实际速度为{2}", set, increase, actual); nums[1] = nums[0]; nums[2] = nums[1]; }//while循环结束括号 Console.WriteLine("无法与 {0} 连接", clientep.Address); //释放资源 client.Close(); newsock.Close(); Console.ReadKey(); //*** //***将得到的实际速度(list集合),再用一次Socket通信,发送给3-5项目接收 成功! //*** IPEndPoint IPep2 = new IPEndPoint(IPAddress.Any, 127); Socket newsock2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock2.Bind(IPep2); newsock2.Listen(10); Console.WriteLine("等待3-5项目客户端的连接"); Socket client2 = newsock2.Accept();//等待客户端的连接 IPEndPoint clientep2 = (IPEndPoint)client2.RemoteEndPoint; Console.WriteLine("与{0}连接在{1}端口", clientep2.Address, clientep2.Port); string welcome2 = "welcome to the second server"; //Thread.Sleep(1000);//延时作用 是线程休息等待1S Stopwatch sw = new Stopwatch();//记录程序的执行时间 sw.Start(); for (int i2 = 0; i2 < 30000; i2++) { for (int j2 = 0; j2 < 20000; j2++) { //空循环 } } sw.Stop(); TimeSpan ts = sw.Elapsed; Console.WriteLine(ts.TotalSeconds); date = new byte[1024]; date = Encoding.ASCII.GetBytes(welcome2);//字符串转换为字节数组 传送给客户端 client2.Send(date, date.Length, SocketFlags.None);//send发送给客户端 //发送实际速度集合的长度 给3-5客户端 成功! for (int i3 = 0; i3 < 10000; i3++) { for (int j3 = 0; j3 < 10000; j3++) { } //空循环 起延迟作用 } date = new byte[1024]; date = Encoding.ASCII.GetBytes(actualSpeed.list.Count.ToString()); client2.Send(date, date.Length, SocketFlags.None); //将每一次的实际速度先转换为string 再转换为字节发送给客户端 用for循环依次传递 成功! //如果是直接将char数组转换为字节 再发送给客户端 发生乱码情况 ?不明白其中错误原因? double[] values = actualSpeed.list.ToArray().Select(o => Convert.ToDouble(o)).ToArray(); string[] strvalue = new string[values.Length]; for (int j = 0; j < values.Length; j++) { strvalue[j] = values[j].ToString();//将实际速度集合转换为string数组 for (int i4 = 0; i4 < 10000; i4++) { for (int j4 = 0; j4 <10000; j4++) { } //空循环 起延迟作用 } date = new byte[1024]; date = Encoding.ASCII.GetBytes(strvalue[j]);//string数组中的每个元素一次转换为字节 发送给客户端 client2.Send(date, date.Length, SocketFlags.None); } //////传送单个数据 最后一个实际速度值 ////date = Encoding.ASCII.GetBytes(actual.ToString());//传送最后一个单个数据 成功! ////client2.Send(date, date.Length, SocketFlags.None); ////client.Send(s, s.Length, SocketFlags.None); Console.WriteLine("传送数据成功!"); client2.Close(); newsock2.Close(); Console.ReadKey(); }//Main函数 }//类 program }//命名空间
客户端-2代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections; namespace _3_5_接收服务器端传回的数据 { class Program { static void Main(string[] args) { ArrayList list = new ArrayList(); byte[] data = new byte[1024]; IPEndPoint IPep2=new IPEndPoint(IPAddress.Parse("127.0.0.1"),127); Socket server2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { server2.Connect(IPep2); for (int i1 = 0; i1 < 10000; i1++) { for (int j1 = 0; j1 < 10000; j1++) { } //空循环 起延迟作用 } } catch (SocketException e) { Console.WriteLine("连接服务器失败"); Console.WriteLine(e.ToString()); Console.ReadKey(); return; } finally { } //接收来自服务器的数据 for (int i2 = 0; i2 < 10000; i2++) { for (int j2 = 0; j2 < 10000; j2++) { } //空循环 起延迟作用 } int receive = server2.Receive(data); string stringData = Encoding.ASCII.GetString(data, 0, receive); Console.WriteLine(stringData);//welcome to the second server ////接收并显示最后一个实际速度 成功! //int rece = server2.Receive(data); //string datavs = Encoding.ASCII.GetString(data, 0, rece);//传送单个数据 即最后一次的实际速度 成功! //Console.WriteLine(datavs); //接收实际速度数组的长度 count 成功! for (int i3 = 0; i3 < 10000; i3++) { for (int j3 = 0; j3 < 10000; j3++) { } //空循环 起延迟作用 } data = new byte[1024]; int rece = server2.Receive(data); string length = Encoding.ASCII.GetString(data, 0, rece);// 成功! Console.WriteLine(length); int n=Convert.ToInt32(length); //依次接收来自服务器端传送的实际速度值 成功! string[] speed = new string[n]; for (int i = 0; i <n ; i++) { for (int i4 = 0; i4 < 10000; i4++) { for (int j4 = 0; j4 < 10000; j4++) { } //空循环 起延迟作用 } data = new byte[1024]; int rece2 = server2.Receive(data); speed[i]=Encoding.ASCII.GetString(data,0,rece2); Console.WriteLine("第{0}次的实际速度是{1}", i+1, speed[i]); } Console.ReadKey(); } } }
这样实现了服务器计算得出的速度值,给新的客户端。但是存在一个问题,就是在与客户端-2传送数据时,有时会发生数据粘包或数据丢失的情况,因此加入了很多的空循环起延时作用,从而可以将每次发送数据之间有时间间隔,从而互不影响,不会再粘包,但是这样会使程序运行效率变低,速度过慢,这样再复杂一些的算法,根本不能做到实时控制,实时传送,实时监控。因此这一点还要想其他的办法解决。