TCP通信模板

一,服务器程序

1.用VS2022创建一个新的winform项目作为服务器

2.将新项目的class Program中的代码修改 

    internal static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //1,TcpListener对socket进行了一层封装,这个类里面自己会去创建socket对象
            TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 7788);

            //2,开始进行监听
            listener.Start();

            //3,等待客户端连接过来
            TcpClient client = listener.AcceptTcpClient();

            //4,取得客户端发送过来的数据
            NetworkStream stream = client.GetStream();//得到了一个网络流  从这个网络流可以取得客户端发送过来的数据

            byte[] data = new byte[1024];//创建一个数据的容器,用来承接数据,此处有大小限制

            Thread t1 = new Thread(() => ReadMsg(stream, data));    //开启线程读取消息
            t1.Start();

            Thread t2 = new Thread(() => SendMsg(stream));    //开始线程发送消息
            t2.Start();
 
 
            //stream.Close();
            //client.Close();
            //listener.Stop();//停止监听
            //Console.ReadKey();

        }

        private static void SendMsg(NetworkStream stream)
        {
            byte[] data = new byte[7] { 0xCB,0x05,0x01,0x00,0x19,0x00,0x20};
            stream.Write(data, 0, data.Length);

            //byte[] data = new byte[255];
            //data[1] =  0xfd;
            //byte[] temp = new byte[100];
            //Array.Copy(data, 0, temp, 0,100 );
            //stream.Write(temp, 0, temp.Length);
            //Thread.Sleep(90);
            //byte[] temp2 = new byte[100];
            //Array.Copy(data, 100, temp2, 0, 100);
            //stream.Write(temp2, 0, temp2.Length);
            //Thread.Sleep(90);
            //byte[] temp3 = new byte[55];
            //Array.Copy(data, 200, temp3, 0, 55);
            //stream.Write(temp3, 0, temp3.Length);
        }

        private static void ReadMsg(NetworkStream stream, byte[] data)
        {
            while (true)
            {
                //0 表示从数组的哪个索引开始存放数据
                //1024表示最大读取的字节数
                int length = stream.Read(data, 0, 1024);//读取数据
                string result = BitConverter.ToString(data).Replace("-", " ");
                Debug.WriteLine(result);
                Thread.Sleep(1000);
            }
        }

    }

二,客户端程序

1.用VS2022创建一个新的winform项目作为客户端

2.将它的class Program做如下修改

    internal static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {

            TCPClient client = new TCPClient("127.0.0.1", 7788);
            if (client.Connect())
                for (int i = 0; i < 10; i++)
                {
                    int[] ret=client.ReadyToStart();
                    foreach (int item in ret)
                    {
                        Console.WriteLine(item);
                    }
                }

        }
    }

3.创建一个新的类,命名为TCPClient 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

class TCPClient
{
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *变量表
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    private TcpClient _client;
    private string _serverIpAddress;
    private int _serverPort;
    private NetworkStream _stream;
    private Sender _sender;
    private Receiver _receiver;
    //发送锁
    public static Boolean TCPSend = false;
    public static Object TCPLockObj = new object();
    //主线程锁
    public static Boolean MainLock = false;
    public static AutoResetEvent MainLockEvent = new AutoResetEvent(false);


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *TCP连接并初始化
     * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    public TCPClient(string serverIpAddress, int serverPort)
    {
        /* * * * * * * * * * * * * * * * * * * * * * * * * * * *客户端的初始化* * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
        _serverIpAddress = serverIpAddress;
        _serverPort = serverPort;

        /* * * * * * * * * * * * * * * * * * * * * * * * * * * *服务器的初始化* * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
        //TcpListener listener = new TcpListener(IPAddress.Parse(serverIpAddress), serverPort);//1,TcpListener对socket进行了一层封装,这个类里面自己会去创建socket对象
        //listener.Start();//2,开始进行监听
        //TcpClient client = listener.AcceptTcpClient();//3,等待客户端连接过来
        //NetworkStream stream = client.GetStream();//4,取得客户端发送过来的数据,得到了一个网络流  从这个网络流可以取得客户端发送过来的数据
    }

    public Boolean Connect()
    {
        try 
        {
            _client = new TcpClient();
            _client.Connect(_serverIpAddress, _serverPort);
            StartSender_StartReceiver();
            Console.WriteLine("已连接到服务器"); 
            return true;
        }
        catch (Exception) 
        {
            return false;
        }
        
    }

    public Boolean Disconnect() 
    {
        try 
        {
            StopSender_StopReceiver();
            _client.Close();
            Console.WriteLine("已断开与服务器的连接");
            return true;
        }
        catch (Exception) 
        { 
            return false; 
        }
        
    }


    public void StartSender_StartReceiver() 
    {

            _stream=_client.GetStream();
            _sender=new Sender(_stream);
            _sender.StartSender();  
            _receiver = new Receiver(_stream);
            _receiver.StartReceiver();
        
    }

    public void StopSender_StopReceiver()
    {
         _sender.StopSender();
        _receiver.StopReceiver();
    }




    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *客户端发送的指令
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    public int[] ReadyToStart() 
    {
        _sender.ReadyToStart();
        MainLock = MainLockEvent.WaitOne(2000);
        int[] ret;
        if (MainLock)
        {
            ret = new int[] {_receiver.X_DIST,_receiver.THETA };
            return ret; 
        }
        else 
            return ret=new int[] { -1,-1 };
    }


}

4.再创建两个新的类Sender、Receiver 

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

class Sender
{

    private Thread SenderThread;
    private CancellationTokenSource _ctsSenderThread;
    private CancellationToken tokenSenderThread;
    private byte[] QueueBytes;
    private ConcurrentQueue<byte[]> CmdQueue = new ConcurrentQueue<byte[]>();


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *初始化
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    private NetworkStream stream;
    public Sender(NetworkStream stream)
    {
        this.stream = stream;
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * * 发送线程
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * */
    public void StartSender()//启动发送者线程
    {
        _ctsSenderThread = new CancellationTokenSource(); // 创建新的 CancellationTokenSource
        tokenSenderThread = _ctsSenderThread.Token;
        SenderThread = new Thread(() => Send(tokenSenderThread)); // 创建新的线程

        SenderThread.Start(); // 启动线程
    }
    public void StopSender()//停止发送者线程
    {
        object objDataSend = SenderThread;
        if (objDataSend != null)//判断dataSend是否为空
        {
            _ctsSenderThread.Cancel();
        }

    }
    private void Lock()
    {
        TCPClient.TCPSend= false;
        // Monitor.Pulse机制,等待两秒钟
        lock (TCPClient.TCPLockObj)
        {
            Monitor.Wait(TCPClient.TCPLockObj, 2000);
            // 检查是否接收到返回结果
            if (!TCPClient.TCPSend)
            {
                Console.WriteLine("服务器未返回");
            }
        }
    }
    void Send(CancellationToken token)
    {
        try
        {
            while (!token.IsCancellationRequested)
            {
                if (CmdQueue.TryDequeue(out QueueBytes))
                {
                    stream.Write(QueueBytes, 0, QueueBytes.Length);

                    Lock();//发送锁,两秒内接收线程接到回复后解锁,若两秒后仍无回复则自动解锁
                }
            }
        }
        catch (ThreadInterruptedException)
        {
            // 处理线程被中断的情况
            Console.WriteLine("发送者线程被中断");
        }
    }


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *指令表
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    byte  HEAD,HEAD_ACTIVE=0xDA,HEAD_UNACTIVE = 0xDB;//报文头
    byte  LEN;//长度位
    byte  CMD;//命令位
    byte  CHECK;//校验位

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * *
     *  * * * * * * * * * ** * * * * * * * * * * * * * * *发送函数
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * */
    public void ReadyToStart()//开始指令
    {
        HEAD = HEAD_ACTIVE;
        LEN = 0x01;
        CMD = 0x02;
        byte[] SendBytes = new byte[] { HEAD,LEN,CMD };
        CmdQueue.Enqueue(SendBytes);
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *校验
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


class Receiver
{
    private Thread  ReceiverThread;
    private CancellationTokenSource  _ctsReceiverThread;
    private CancellationToken  tokenReceiverThread;
    private int _delayTime=50;//监听到数据流有数据后,接收线程中断_delayTime(ms),以便数据流有足够的时间传输完整的报文到客户端,
                               //即监听到数据流有数据后等待_delayTime(ms)客户端再读取stream中的数据
    private int _lengthOfLongestCMD=167;//最长的指令的长度
    private int _redundancy=100;//冗余,加在_lengthOfLongestCMD后面,以便扩展缓存buffer的长度
    private int _outOfLENRange=2;//LEN长度位记录CMD命令位+DATA数据位总长度,不包括HEAD报文头+LEN长度位这两个字节,故_outOfLENRange=2
    private int _path=0;
    private int _tempLength;
    private byte[] _tempBuffer,buffer,_complete;
    List<byte> _combinedList;
    



    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *初始化
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    private NetworkStream stream;

    public Receiver(NetworkStream stream) { this.stream = stream; }




    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *TCP数据接收
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    public void StartReceiver()//启动接收者线程
    {
        _ctsReceiverThread = new CancellationTokenSource(); // 创建新的 CancellationTokenSource
        tokenReceiverThread = _ctsReceiverThread.Token;
        ReceiverThread = new Thread(() => TCP_DataReceived(tokenReceiverThread));
        ReceiverThread.Start(); // 启动线程
    }
    public void StopReceiver()//停止接收者线程
    {
        object objDataReceive = ReceiverThread;
        if (objDataReceive != null)//判断dataSend是否为空
        {
            _ctsReceiverThread.Cancel();
        }

    }
    private void UnLock()//释放发送锁,使发送线程可以发送下一个指令
    {
        lock (TCPClient.TCPLockObj)
        {
            TCPClient.TCPSend = true;
            Monitor.Pulse(TCPClient.TCPLockObj);
        }
    }

    private void TCP_DataReceived(CancellationToken token)//数据接收
    {
        while (!token.IsCancellationRequested)
        {
            if (stream.DataAvailable)
            {
                Thread.Sleep(_delayTime);
                try
                {               
                    buffer = new byte[_lengthOfLongestCMD+_redundancy];
                    int length = stream.Read(buffer, 0, buffer.Length);//读取数据
                    
                    switch(_path)//处理报文接收不完整机制
                    {
                        case 0:
                            byte[] b = new byte[] { buffer[1] ,0x00,0x00,0x00};//小端表示
                            _tempLength = BitConverter.ToInt32(b, 0);//将字节数组转化为整数,其中b必须是四字节否则会报错
                            Console.WriteLine("长度位规定的长度为: " + _tempLength);
                            //判断是否接收到完整的报文
                            if (length-_outOfLENRange == _tempLength) //接收到的报文长度等于长度位所规定的长度
                            {
                                _complete=new byte[length];
                                Array.Copy(buffer, 0, _complete, 0, length);
                                string result = BitConverter.ToString(_complete).Replace("-", " ");
                                Console.WriteLine("一次成功:"+result);
                                UnLock();//释放发送锁,使发送线程可以发送下一个指令
                                DataAnalysis(_complete);//分析报文数据
                            }
                            else if (length - _outOfLENRange > _tempLength)//接收到的报文长度超过长度位所规定的长度
                            {
                                Console.WriteLine("0报文长度过长,无效");
                                UnLock();//释放发送锁,使发送线程可以发送下一个指令
                            }
                            else //接收到的报文长度少于长度位所规定的长度
                            {
                                _tempBuffer = new byte[length];
                                Array.Copy(buffer, 0, _tempBuffer, 0, length);
                                _combinedList = new List<byte>(_tempBuffer);
                                _path = 1;
                            }
                            break;


                        case 1:
                            _tempBuffer = new byte[length];
                            Array.Copy(buffer, 0, _tempBuffer, 0, length);
                            _combinedList.AddRange(_tempBuffer);
                            if (_combinedList.Count - _outOfLENRange == _tempLength)//接收到完整报文
                            {
                                _complete = _combinedList.ToArray();
                                string result = BitConverter.ToString(_complete).Replace("-", " ");
                                Console.WriteLine(result);
                                _path = 0;
                                UnLock();//释放发送锁,使发送线程可以发送下一个指令
                                DataAnalysis(_complete);//分析报文数据
                            }
                            else if(_combinedList.Count - _outOfLENRange > _tempLength)//接收到的报文长度超过长度位所规定的长度
                            {
                                Console.WriteLine("1报文长度过长,无效");
                                _path = 0;
                                UnLock();//释放发送锁,使发送线程可以发送下一个指令
                            }
                            break;
                    }
                }
                catch (IndexOutOfRangeException Ine)
                {
                    Console.WriteLine("索引超出数组界限错误:" + Ine.Message);
                    // 处理索引超出数组界限的错误,例如回退到默认处理或给出提示信息等
                }
            }
        }
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *数据解析
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    private void DataAnalysis(byte[] _analysis)
    {
        //分析所获取的报文数据
        switch (_analysis[0]) 
        {
            case 0xCB:
                switch (_analysis[2]) 
                {
                    case 0x01:
                        x_dist = BitConverter.ToInt32(new byte[] { _analysis[4], _analysis[3], 0x00, 0x00 }, 0);
                        theta = BitConverter.ToInt32(new byte[] { _analysis[6], _analysis[5], 0x00, 0x00 }, 0);
                        TCPClient.MainLockEvent.Set();
                        break;
                }
                break;

            case 0xCA:
                break;
        }
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
    *  * * * * * * * * * ** * * * * * * * * * * * * * *解析所得数据
    * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
    private int x_dist;

    public int X_DIST
    {
        get { return x_dist; }
        set { x_dist = value; }
    }

    private int theta;

    public int THETA
    {
        get { return theta; }
        set { theta = value; }
    }



}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值