通訊編程上位機軟體實現(SOCKET)——第二回

來源:互聯網
上載者:User

這篇廢話不多說,直接上代碼。

首先說明,通訊過程中的異常均不進行處理(串連異常除外),由逾時重發控制。

 

一、擷取SOCKET串連類TimeOutSocket

 

public class TimeOutSocket    {        private static bool IsConnectionSuccessful = false;//串連是否成功        private static Exception socketexception;        private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);         public static Socket Connect(IPAddress ipAddress, int port, int timeoutMSec)        {            TimeoutObject.Reset();//將事件設為非終止狀態,阻止線程            socketexception = null;             Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            //非同步串連            temp.BeginConnect(new IPEndPoint(ipAddress, port), new AsyncCallback(CallBackMethod), temp);             if (TimeoutObject.WaitOne(timeoutMSec, false))            {                //在連線逾時時間範圍內等待訊號量為真則返回SOCKET                if (IsConnectionSuccessful)                {                    return temp;                }                else                {                    throw new TimeoutException("串連遠程主機失敗!請檢查網路連接是否通暢。");                }            }            else            {                //關閉串連,並釋放所有相關資源                temp.Close();                throw new TimeoutException("連線逾時!請檢查網路連接是否通暢。");            }        }         /// <summary>        /// 非同步串連成功後回呼函數        /// </summary>        /// <param></param>        private static void CallBackMethod(IAsyncResult asyncresult)        {            try            {                IsConnectionSuccessful = false;                Socket client = asyncresult.AsyncState as Socket;                //結束掛起的非同步串連請求                client.EndConnect(asyncresult);                IsConnectionSuccessful = true;            }            catch (Exception ex)            {                IsConnectionSuccessful = false;                socketexception = ex;            }            finally            {                //允許其它線程繼續                TimeoutObject.Set();            }        }}

 

二、通訊變數(常量)

 

private const int ConnectTimeOut = 6000;//連線逾時時間,以ms為單位private byte[] data = new byte[1024];//接收位元組數組private int recvSize = 1024;//接收資料個數private static int XH = 1;//發送的命令序號 //下面兩個類庫ConvertLibrary和DealProtocolData是我封裝的類庫,在後續中將逐一給出ConvertLibrary convertData = new ConvertLibrary();//對資料提供轉換支援DealProtocolData dealProtocolData = new DealProtocolData();//按要求處理協議結果  

  

 

三、委託

 

委託,實質上就是指向函數的指標,也是一個類。

        delegate void DealRecvMsgHandler(string str);//用於處理接收到的資料

 

上面一句用ILDASM反編譯後如下:

 

可以看出,系統預設產生了其建構函式(.ctor是建構函式),以及成員函數BeginInvoke、EndInvoke以及Invoke。自己定義的委託繼承於System.MulticastDelegate(反編譯中extends看出)。

 

 

四、非同步接收資料回呼函數ReceivData()

 

/// <summary>        ///非同步接收資料回調        /// </summary>        /// <param></param>        private void ReceivData(IAsyncResult iar)        {            Socket remote = (Socket)iar.AsyncState;            int recv = 0;            try            {                recv = remote.EndReceive(iar);//異常:遠程主機強迫關閉了一個現有的串連            }            catch            {            }            string tempStr = convertData.ByteToHex(data, recv);//位元組數組轉化成16進位             if (tempStr.Length > 0)            {                         // DealRecvMsg資料處理函數                     DealRecvMsgHandler handler = new DealRecvMsgHandler(DealRecvMsg);                handler.Invoke(tempStr);//同步資料處理            }            else            {               //由於用了第三方的串口轉網口模組,所以有時候會收到空的情況,這時繼續接收                    try                    {                        remote.BeginReceive(data, 0, recvSize, SocketFlags.None, new AsyncCallback(ReceivData), PublicCommunication.client);                    }                    catch { }            }        }

 

五、非同步發送資料回呼函數(SendData)

 

/// <summary>        ///非同步發送資料回調        /// </summary>        /// <param></param>        private void SendData(IAsyncResult iar)        {            Socket remote = (Socket)iar.AsyncState;            try            {                int send = remote.EndSend(iar);                 currentSendedOrderDate = DateTime.Now;//記錄下發送時間,用於重發時使用                if (listenerTimer.Enabled == false)// listenerTimer用於逾時重發                    listenerTimer.Enabled = true;                currentSendedOrderEnum = GetSendedOrderType(sendMsg);//擷取已發送命令類型                currentSendedOrderXH = GetSendedOrderXH(sendMsg);//擷取已發送命令序號  //發送後,非同步接收                remote.BeginReceive(data, 0, recvSize, SocketFlags.None, new AsyncCallback(ReceivData), PublicCommunication.client);            }            catch            {            }        }

 

 

 

六、接收資料處理函數(只判斷命令的合法性,決定下一個命令。具體的資料需要另需委託進行處理。)

 

private void DealRecvMsg(string orgTempStr)        {//這裡面是一個while迴圈,迴圈的條件是命令長度大於某個最小指,每次迴圈按命令包中命令長度取出命令,並將剩餘的接收到的命令用於迴圈。//由於通訊追循一定的流程,所以在這裡由接收到的命令可以決定下一個命令//大致給一下這裡面的程式 while (orgTempStr.Length >= 12)                    {                        try                        {                            string tempStr = orgTempStr.Substring(0, int.Parse(convertData.ConvertString(orgTempStr.Substring(2, 2), 16, 10)) * 2);//取出一條命令                            orgTempStr = orgTempStr.Substring(int.Parse(convertData.ConvertString(orgTempStr.Substring(2, 2), 16, 10)) * 2);// orgTempStr保留剩餘的命令                             //判斷資料合法性:根據協議接收到的資料長度至少為12,而且保證接收到的資料無誤(校正碼), GetStrXOR()函數是擷取校正碼,後面給出                             if (tempStr.Length >= 12                                && dealProtocolData.GetStrXOR(tempStr.Substring(0, tempStr.Length - 2)).Equals(tempStr.Substring(tempStr.Length - 2), StringComparison.OrdinalIgnoreCase))                            {                                string workState = tempStr.Substring(8, 2);//擷取儀器工作狀態                                string order = tempStr.Substring(4, 2);//擷取命令字                                 //如果不是上次發送的命令則丟棄當前資料                                if (!JudgeReceiveOrderWithSendOrder(order, currentSendedOrderEnum))                                    continue;                                //命令序號不同則丟棄                                if (!(int.Parse(convertData.ConvertString(tempStr.Substring(6, 2), 16, 10)) == currentSendedOrderXH))                                    continue;                                 currentSendedOrderDate = DateTime.MinValue;//收到命令後,設定命令的發送時間為DateTime的最小值                                ReSendTimeCount = -1;//逾時重發次數 //"00"應該定義成常量,方便使用時如果"00"改為”ff”時不用滿程式中修改                                if (workState.Equals("00", StringComparison.OrdinalIgnoreCase))//儀器正常工作                                {if (order.Equals(ProtocolContent.SetParamsCommandWord, StringComparison.OrdinalIgnoreCase))//設定參數命令的返回                                    {//決定下一個要發送的命令sendMsg =……;}………..……….. byte[] msg = convertData.HexToByte(sendMsg);                                    try                                    {                                        //非同步發送資料                                        PublicCommunication.client.BeginSend(msg, 0, msg.Length, SocketFlags.None, new AsyncCallback(SendData), PublicCommunication.client);                                         //處理髮送序號,保證發送序號在1-255之間                                        XH = (XH + 1) % 256;                                        if (XH == 0)                                            XH = 1;                                    }                                    catch                                    {                                    } }

 

 

七、逾時重發

 

/// <summary>    /// 命令的枚舉值    /// </summary>    public enum OrderEnum    {        UnKnown = 0    } private static string sendMsg = String.Empty;//記錄每次發送的命令private static OrderEnum currentSendedOrderEnum = OrderEnum.UnKnown;//記錄本次發送的命令的類型private static int currentSendedOrderXH = -1;//當前發送命令的序號private static DateTime currentSendedOrderDate = DateTime.MinValue;//最近一次發送命令的時間private System.Timers.Timer listenerTimer;//用於監控命令發送的定時器private static int ReSendTimeCount = -1;//重新發送命令的次數  listenerTimer = new System.Timers.Timer(25);listenerTimer.Elapsed += new System.Timers.ElapsedEventHandler(listenerTimer_Elapsed);listenerTimer.Enabled = false; private void listenerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)        {            if (currentSendedOrderDate != DateTime.MinValue)//當沒有收到資料時            {                double timeInterval = (DateTime.Now - currentSendedOrderDate).TotalMilliseconds;                if (timeInterval >= 1000)//1S重發                {                    ReSendTimeCount++;                    if (ReSendTimeCount >= 3)                    {                        //逾時處理……..                        return;                    }//未逾時三次,繼續重發                    SendMsgWhenTimeOut(sendMsg);                }            }        }

 

八、通訊結束或者逾時後的處理

 

當無通訊時,用戶端關閉,伺服器是檢測不到的,所以我在做的時候,一次通訊結束或者逾時後都會將串連關閉共置為空白。

private void WhenCommunicateOver(){listenerTimer.Enabled = false;try            {                PublicCommunication.client.Close();            }            catch { }            finally            {                PublicCommunication.client = null;            }}

 

九、嘗試三次串連

 

private void startCommunication()        {            int connectTime = 0;            do            {                try                {                    PublicCommunication.client = TimeOutSocket.Connect(IPAddress.Parse(PublicModel.InstrumentIP), PublicModel.InstrumentPort, ConnectTimeOut);                                        break;                }                catch (Exception ec)                {                    connectTime++;                    PublicCommunication.client = null;                    if (connectTime >= 3)                    {                        WhenCommunicateOver();                        lock (whetherShowErrorMsg)                        {                            if (!((bool)whetherShowErrorMsg))                            {                                MessageBox.Show(this, ec.Message, "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Error);                                whetherShowErrorMsg = true;                            }                        }                        Application.DoEvents();                        return;                    }                }            }            while (connectTime < 4);}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.