Socket通信

Posted 念苏苏0504

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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传送数据时,有时会发生数据粘包或数据丢失的情况,因此加入了很多的空循环起延时作用,从而可以将每次发送数据之间有时间间隔,从而互不影响,不会再粘包,但是这样会使程序运行效率变低,速度过慢,这样再复杂一些的算法,根本不能做到实时控制,实时传送,实时监控。因此这一点还要想其他的办法解决。

以上是关于Socket通信的主要内容,如果未能解决你的问题,请参考以下文章

socket通信

与另一个片段通信的片段接口

Python 之 Socket编程(TCP/UDP)

C#:Socket通信

Socket通信客户端和服务端代码

socket网络编程:在简单套接字基础上加上通信循环(代码完善)