C# DotNetty TCP對接 松下扣料機


我對TCP Socket本身了解並不深入,但是這回真碰上了那也只能硬着頭皮上了。

而且松下扣料機的廠商人員只來一天半,也就是要在一天內完成對接,你只能通過以往的報文日志來分析大致的通訊情況,松下廠商並不提供模擬測試程序。

很扯淡,而且要用C#實現,就差沒罵出來了,Java的Netty好歹寫過,現在要去看那個資料少的可憐的DotNetty!

閉門造車寫了幾天,在正式對接那天血崩,意料之中。

說下崩在哪。

松下報文格式是這樣的:

心跳PING:

(文檔版):

你以為是什么?

XML字符串嗎?我是這么想的,但是對接那天我的程序始終無法和扣料機發生心跳,一發生心跳就斷開連接,一群人在我電腦和扣料機跑來跑去看日志服了。

報文有問題。

<STX>8<SHO>PING_REQ<ETX>

不是字符串,不是XML,而是ASCII碼。

 		public static byte STX =  0x02;
        public static byte SOH = 0x01;
        public static byte ETX = 0x03;
        public static char STX_CHAR = '\u0002';
        public static char SHO_CHAR = '\u0001';
        public static char ETX_CHAR = '\u0003';

將0X02對應字符粘貼到notepad++上。

無語了。

然后改程序,算是勉強完成心跳,因為和Netty一樣有自動彈射心跳所以不難。

難得是解碼器,在Socket通訊中要考慮以下情況。

( tcp是流,沒有界限.也就沒所謂的包.這里包,算是約定)

粘包、多包和少包, 斷包

<message>
 <header messageClass="530" transactionID="1234567890" reply="0">
 <location routeID="1001" routeName="LINE1" equipmentID="1000"
 equipmentName="equip1" zoneID="1001" zonePos="2" zoneName="SMT1" laneNo="0"
controllerGuid="abcd12345678"/>
 </header>
 <body>
 <result errorCode="0" errorText="" actionCode=""/>
</body>
</message>
<message>
 <header messageClass="530" transactionID="1234567890" reply="0">
 <location routeID="1001" routeName="LINE1" equipmentID="1000"
 equipmentName="equip1" zoneID="1001" zonePos="2" zoneName="SMT1" laneNo="0"
controllerGuid="abcd12345678"/>
 </header>
 <body>
 <result errorCode="0" errorText="" actionCode=""/>
</body>
</message>
<message>
 <header messageClass="530" transactionID="1234567890" reply="0">
 <location routeID="10
--第一次發送--
01" routeName="LINE1" equipmentID="1000"
 equipmentName="equip1" zoneID="1001" zonePos="2" zoneName="SMT1" laneNo="0"
controllerGuid="abcd12345678"/>
 </header>
 <body>
 <result errorCode="0" errorText="" actionCode=""/>
</body>
</message>
--第二次發送--

總之有許多情況要考慮,報文提供有長度,我開始也是通過長度去讀取報文,但是我程序算出來的長度和扣料機發過來的報文長度是不一樣的,不清楚是什么原因。

最后不用它的長度,定義緩沖區,當有消息進來解碼器while處理,將所有情況走一遍,而且如果有多個連接,能夠保證在同時粘包斷包的情況下互相不影響,每個連接無論它發再shit的包,我都能處理。

  /// <summary>
    /// 解碼
    /// </summary>
    public class DecoderHandler : ByteToMessageDecoder
    {
        IByteBuffer byteBuffer = ByteBufferUtil.DefaultAllocator.Buffer();
        public Int32 PipleLineId = 0;

        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
        {
            int len = input.ReadableBytes;
            byte[] bytes = new byte[len];
            input.GetBytes(0, bytes);
            SimpleLogHelper.LogInfo("PipleLineId = " + PipleLineId + "接收信息 : " + Encoding.Default.GetString(bytes));

            while (bytes.Length > 0)
            {
             SimpleLogHelper.LogInfo("PipleLineId = " + PipleLineId + "本次處理 : " + Encoding.Default.GetString(bytes));
                int stxIndex = Array.IndexOf(bytes, SocketHelper.STX);
                int etxIndex = Array.IndexOf(bytes, SocketHelper.ETX);

                //緩存沒有
                if (byteBuffer.ReadableBytes == 0)
                {
                    //有頭
                    if (stxIndex != -1)
                    {
                        //有尾
                        if (etxIndex != -1)
                        {
                            int shoIndex = Array.IndexOf(bytes, SocketHelper.SOH);
                            byte[] bodyLen = bytes.Skip(stxIndex + 1).Take(shoIndex - stxIndex - 1).ToArray();
                            byte[] message = bytes.Skip(shoIndex + 1).Take(etxIndex - shoIndex - 1 ).ToArray();
                            Package package = new Package();
                            package.length = int.Parse(Encoding.Default.GetString(bodyLen));
                            package.messageBody = Encoding.Default.GetString(message);
                            SocketHelper.SendPackage(package, output);
                            bytes = bytes.Skip(etxIndex + 1).ToArray();
                        }
                        else
                        {
                            //如果stxIndex 頭部前有報文,丟棄前面的報文
                            byteBuffer.WriteBytes(bytes.Skip(stxIndex).ToArray());

                            int tmpLen = byteBuffer.ReadableBytes;
                            byte[] tmpBytes = new byte[tmpLen];
                            byteBuffer.GetBytes(0, tmpBytes);
                            SimpleLogHelper.LogInfoSuccess("PipleLineId = " + PipleLineId + "緩存1 : " + Encoding.Default.GetString(tmpBytes));

                            bytes = new byte[0];
                        }
                    }
                    else
                    {
                        //緩存為空,報文沒頭,跳過
                        bytes = new byte[0];
                    }
                }
                else
                {
                    if (stxIndex != -1)
                    {
                        if (stxIndex < etxIndex || etxIndex == -1)
                        {
                            int tmpLen = byteBuffer.ReadableBytes;
                            byte[] tmpBytes = new byte[tmpLen];
                            byteBuffer.GetBytes(0, tmpBytes);
                            SimpleLogHelper.LogError("PipleLineId = " + PipleLineId + "丟棄 : " + Encoding.Default.GetString(tmpBytes));

                            bytes = bytes.Skip(stxIndex).ToArray();
                            byteBuffer.Clear();
                            continue;
                        }
                    }

                    if (etxIndex != -1)
                    {
                        byteBuffer.WriteBytes(bytes.Take(etxIndex + 1).ToArray());

                        int tmpLen = byteBuffer.ReadableBytes;
                        byte[] tmpBytes = new byte[tmpLen];
                        byteBuffer.GetBytes(0, tmpBytes);
                        SimpleLogHelper.LogInfoSuccess("PipleLineId = " + PipleLineId + "緩存3 : " + Encoding.Default.GetString(tmpBytes));

                        int buffLen = byteBuffer.ReadableBytes;
                        byte[] currentBytes = new byte[buffLen];
                        byteBuffer.GetBytes(0, currentBytes);
                        int shoIndex = Array.IndexOf(currentBytes, SocketHelper.SOH);
                        byte[] bodyLen = currentBytes.Skip(1).Take(shoIndex - 1).ToArray();
                        byte[] message = currentBytes.Skip(shoIndex + 1).SkipLast(1).ToArray();

                        Package package = new Package();
                        package.length = int.Parse(Encoding.Default.GetString(bodyLen));
                        package.messageBody = Encoding.Default.GetString(message);
                        SocketHelper.SendPackage(package, output);

                        byteBuffer.Clear();
                        bytes = bytes.Skip(etxIndex + 1).ToArray();
                    }
                    else
                    {
                        //斷包 進緩沖
                        byteBuffer.WriteBytes(bytes);
                        bytes = new byte[0];

                        int tmpLen = byteBuffer.ReadableBytes;
                        byte[] tmpBytes = new byte[tmpLen];
                        byteBuffer.GetBytes(0, tmpBytes);
                        SimpleLogHelper.LogInfoSuccess("PipleLineId = " + PipleLineId + "緩存2 : " + Encoding.Default.GetString(tmpBytes));
                    }
                }
            }
            input.SkipBytes(len);
        }
    }


免責聲明!

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



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