完整的Socket代碼


先上圖

列舉一個通信協議

網關發送環境數據

 此網關設備所對應的所有傳感器參數,格式如下:

網關發送:

包長度+KEY+請求類型+發送者+接收者+消息類型+消息內容

說明:

包長度:short int型(16位),除本字段外,后面所跟的所有數據字節數;

標識符:int32型,固定值:0x987656789;

請求類型:int32型,數據通信:3;

發送者:string,終端編號,如:11170303001,格式:長度(int)+發送者字符串的Unicode編碼(長度是指發送者的Unicode編碼后的字節數);

接收者:string,服務器編號,如:server,格式:長度(int)+發送者字符串的Unicode編碼(長度是指發送者的Unicode編碼后的字節數);

消息內型:int32型,當前環境參數:146;

 

消息內容:所有控制口開關狀態及環境參數,其格式如下

輸出狀態+輸入狀態+傳感器數量N+傳感器數據 * N

說明

輸出狀態:控制器開關量輸出狀態,long 型(64位),此變量每一位表示一個開關的狀態,1表示開,0表示關;

輸入狀態:控制器開關量輸入狀態,long 型(64位),此變量每一位表示一個輸入的狀態,1表示有輸入,0表示無輸入。最低位用於手自動狀態指示。

傳感器數量:byte型,指的是傳感器數量

傳感器數據:格式為:位置+類型+地址+參數個數+數據

說明

位置:byte型,表示此組傳感器所擺放位置;如:1表示放置點為1區;

類型:byte

地址:byte型,此組傳感器的地址,每個終端所管理的傳感器中不能有重復地址;

參數個數:byte,每個參數對應一個Float數據

數據: float數組,數組元素個數由參數個數決定

 

服務器應答:

包長度+KEY+應答類型

 數據包長度:int16,除本字段外其他所有字段的字節                                     總數;

 KEY值:int32,值=123454321;

      應答類型:int32,值=100:發送成功,可根據此應答                       判斷客戶端是否在線

//---------------------------------------------------------------------------------我是華麗的分割線--------------------------------------------------------------------------------------------------

核心代碼

 public class StaticUdpDal
    {
        private static Socket socket = null;

        private bool IsRun = true, _IsSuccse = false;
        private int num = 0, recNum;
        private int _LoopNum = 3;//循環計數器和循環等待次數
        private bool _IsReadMsgType = true;//是否讀取消息類型

        public int LoopNum
        {
            get { return _LoopNum; }
            set { _LoopNum = value; }
        }
        private EndPoint point;

        /// <summary>
        ///  是否成功收到消息
        /// </summary>
        public bool IsSuccse
        {
            get { return _IsSuccse; }
            set { _IsSuccse = value; }
        }
        private ProtocolModel pModel;
        /// <summary>
        /// 數據發送接收之間需要的數據模型
        /// </summary>
        public ProtocolModel PModel
        {
            get { return pModel; }
            set { pModel = value; }
        }

        private MemoryStream sendStream;

        /// <summary>
        /// //發送內容
        /// </summary>
        public MemoryStream SendStream
        {
            get { return sendStream; }
            set { sendStream = value; }
        }

        /// <summary>
        /// 是否讀取消息類型,默認讀取
        /// </summary>
        public bool IsReadMsgType
        {
            get { return _IsReadMsgType; }
            set { _IsReadMsgType = value; }
        }

        public StaticUdpDal()
        {
            //綁定IP端口
            if (socket == null)
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

                string UdpSendIP = ConfigurationManager.AppSettings["UdpSendIP"].ToString();
                string UdpSendPort = ConfigurationManager.AppSettings["UdpSendPort"].ToString();

                IPEndPoint ipep = new IPEndPoint(string.IsNullOrWhiteSpace(UdpSendIP) ? IPAddress.Any : IPAddress.Parse(UdpSendIP),
                    string.IsNullOrWhiteSpace(UdpSendPort) ? 50090 : int.Parse(UdpSendPort));//本機預使用的IP和端口
                socket.Bind(ipep);
            }
        }

        /// <summary>
        /// UDP發送接收主函數
        /// </summary>
        public BinaryReader UdpMain()
        {
            Task<BinaryReader> taskRecive = Task<BinaryReader>.Factory.StartNew(() => ReciveMsg());
            Task taskSend = Task.Factory.StartNew(() => sendMsg());

            taskRecive.Wait();
            return taskRecive.Result;
        }
        /// <summary>
        /// 向特定ip的主機的端口發送數據報
        /// </summary>
        public void sendMsg()
        {
            while (true)
            {
                if (num >= _LoopNum || !IsRun)
                {
                    IsRun = false;
                    return;
                }
                num++;

                try
                {
                    byte[] buffer = sendStream.ToArray();
                    point = new IPEndPoint(IPAddress.Parse(pModel.DisplayIP), pModel.DisplayPort);
                    socket.SendTo(buffer, buffer.Length, SocketFlags.None, point);//發送
                }
                catch (Exception ex)
                {
                    ZP.Comm.ErrHandler.WriteError(ex, "UDP通信異常-發送");
                    IsRun = false; return;
                }
                Thread.Sleep(1000);//時間間隔為1秒
            }

        }

        /// <summary>
        /// 接收發送給本機ip對應端口號的數據報
        /// </summary>
        public BinaryReader ReciveMsg()
        {
            byte[] data = new byte[1500];

            while (true)
            {
                recNum++;
                if (recNum > 30 || !IsRun) // 3秒未接收到消息,或標記變為停止,停止
                {
                    IsRun = false;
                    return null;
                }

                if (socket == null || socket.Available < 1)
                { Thread.Sleep(200); continue; }
                try
                {
                    int len = socket.Receive(data);//接收  socket.Receive(data,SocketFlags.None);  //
                }
                catch (Exception ex)
                {
                    // 在出現未處理的錯誤時運行的代碼
                    ZP.Comm.ErrHandler.WriteError(ex, "UDP通信異常-接收");
                    IsRun = false; return null;
                }

                BinaryReader reader = GetReciveData(data);//基礎判斷
                if (!_IsSuccse)
                { Thread.Sleep(200); continue; }

                //socket.Close();
                IsRun = false;//修改運行標記
                return reader;
            }
        }

        /// <summary>
        /// 根據模型獲取基礎發送內容
        /// </summary>
        /// <returns></returns>
        public MemoryStream GetBaseContent(ProtocolModel _pModel)
        {
            pModel = _pModel;
            sendStream = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(sendStream);

            writer.Write(System.Net.IPAddress.HostToNetworkOrder((short)0));//長度
            writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.Key));//key
            writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.TalkType));//請求類型

            writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.Sender.Length * 2));//發送者
            writer.Write(Encoding.BigEndianUnicode.GetBytes(pModel.Sender));

            writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.Receiver.Length * 2));//接收者
            writer.Write(Encoding.BigEndianUnicode.GetBytes(pModel.Receiver));

            writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.MsgType));//消息類型
            if (pModel.UseType == 2)
            {
                writer.Write(System.Net.IPAddress.HostToNetworkOrder((int)pModel.DisplayNo.Length * 2)); //終端編號 日光溫室
                writer.Write(Encoding.BigEndianUnicode.GetBytes(pModel.DisplayNo));
            }

            return sendStream;
        }

        /// <summary>
        /// 根據當前模型,獲取接受信息
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public BinaryReader GetReciveData(byte[] data)
        {
            MemoryStream stream2 = new MemoryStream(data);
            BinaryReader reader = new BinaryReader(stream2);

            short packageLen = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt16());//長度

            int key = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt32());//key
            if (!((!pModel.IsConServer && key == (int)GhMsgEnum.Key) || (pModel.IsConServer && key == (int)GhMsgEnum.Key1)))//987656789  123454321
                return reader;
            //-----------------------------------------不同的地方--------------------------------------------------
            int replyKind = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt32());//回復的類型 3
            if (replyKind != PModel.AnswerType)//3
                return reader;
            int senderLen = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt32());//發送者的長度
            byte[] temp = new byte[1024];
            temp = reader.ReadBytes(senderLen);
            string No = Encoding.BigEndianUnicode.GetString(temp);

            int receiveLen = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt32());//接收者的長度
            string receive;
            if (receiveLen > 0)
                receive = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(receiveLen));
            //-----------------------------------------不同的地方--------------------------------------------------
            if (IsReadMsgType)
            {
                int msgType = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt32());//消息類型 160 設備信息
                if (msgType != PModel.AnswerMsgType)//160,155,156,158
                    return reader;
            }
            //獲取公用參數
            _IsSuccse = true;
            return reader;
        }
    }

 

  最后列舉剛剛的通信協議調用部分代碼

/// <summary>
        /// 寫入手動控制操作信息  文檔2 手動控制,打開關閉設備; 未完成(檢查開關狀態)
        /// </summary>
        /// <param name="GreenhouseID"></param>
        /// <param name="UserName"></param>
        /// <param name="isDoing">true:打開;false:關閉</param>
        /// <param name="coilStatus"></param>
        public long WriteDevice(int GreenhouseID, string UserName, bool isDoing, long coilStatus)
        {
            GreenHouseDal ghDal = new GreenHouseDal();
            D_GreenhouseInfo ghModel = ghDal.GetModel(new D_GreenhouseInfo() { GreenhouseID = GreenhouseID });//獲取終端
            ProtocolModel pModel = new ProtocolModel()
            {
                UseType = 1,//使用類型,溫室
                IsConServer = ghModel.ConnectType != 1,//是否服務器轉發
                TalkType = (int)GhMsgEnum.TALK,//請求類型
                AnswerType = (int)GhMsgEnum.TALK,//應答類型

                Sender = string.IsNullOrWhiteSpace(UserName) ? "server" : UserName,//發送者
                Receiver = (ghModel.ConnectType != 1) ? ghModel.DisplayNo : "dev0",//接收者,網關編號
                MsgType = isDoing ? (int)GhMsgEnum.SWITCHCTLON : (int)GhMsgEnum.SWITCHCTLOFF,//消息類型為  512、打開開關;513、關閉開關
                AnswerMsgType = (int)GhMsgEnum.OutputCoil,//應答類型

                //DisplayIP ="192.168.101.31",// (ghModel.ConnectType != 1) ? _DisplayIP : ghModel.DisplayIP,//目標IP
                //DisplayPort =8085// (ghModel.ConnectType != 1) ? _DisplayPort : (int)ghModel.DisplayPort,//目標端口

                DisplayIP = (ghModel.ConnectType != 1) ? _DisplayIP : ghModel.DisplayIP,//目標IP
                DisplayPort = (ghModel.ConnectType != 1) ? _DisplayPort : (int)ghModel.DisplayPort,//目標端口
            };

            StaticUdpDal bud = new StaticUdpDal();
            MemoryStream stream = bud.GetBaseContent(pModel);//獲取基礎發送內容

            BinaryWriter writer = new BinaryWriter(stream);
            writer.Write(System.Net.IPAddress.HostToNetworkOrder((long)coilStatus));//寫入消息內容

            writer.Seek(0, SeekOrigin.Begin);
            writer.Write(System.Net.IPAddress.HostToNetworkOrder((short)(stream.Length - 2)));//寫入長度
            bud.SendStream = stream;
            bud.LoopNum = 1;

            BinaryReader reader = bud.UdpMain();//開始發送和接收
            if (reader == null || !bud.IsSuccse) return (long)-1;//---------------------------------?

            long OutputStatus = System.Net.IPAddress.NetworkToHostOrder(reader.ReadInt64());
            return OutputStatus;
        }

百度上有很多講 socket通信的,都比較基礎,我也是看着別人的代碼總結到自己的項目中的,這里有個關鍵點就是聲明一個靜態的socket變量,並且使用之后並不關閉,而是常駐內存,收到信號就開啟就收線程,然后再將信號轉發給服務器。之前做的都是socket使用后立馬關閉連接,反而會導致服務器內存使用居高不下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM