zoukankan      html  css  js  c++  java
  • 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);
            }
        }
    
  • 相关阅读:
    父亲节前参考四级考试
    rpm小解
    oracle忘记sys/system/scott用户的密码怎么办
    yum 小解
    linux下设置swap文件
    启动mysql 报错: ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)
    mysql 常用命令
    wget安装
    删除mysql
    什么是swap分区
  • 原文地址:https://www.cnblogs.com/yangchaojie/p/14397509.html
Copyright © 2011-2022 走看看