用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代碼:spa
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傳送數據時,有時會發生數據粘包或數據丟失的狀況,所以加入了不少的空循環起延時做用,從而能夠將每次發送數據之間有時間間隔,從而互不影響,不會再粘包,可是這樣會使程序運行效率變低,速度過慢,這樣再複雜一些的算法,根本不能作到實時控制,實時傳送,實時監控。所以這一點還要想其餘的辦法解決。pwa