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代碼: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

相關文章
相關標籤/搜索