工业数采引擎-通信链路SOCKET

通信库:DotNetty

封装实现:TcpServer、TcpClient、Udp

TCP协议特性:面向连接协议;每个新连接都会创建独立的ChannelHandler实例;TcpHandler构造函数在每次客户端连接时触发
UDP协议特性:无连接协议;整个服务端只创建单例UdpHandler;所有UDP数据包共享同一个处理器实例;UdpHandler构造函数仅在服务启动时触发一次

Server
1. 端口复用
2. 定时清理链路

TcpClient
1. 指定本地ip及端口
2. 自动重连

其他
1. 上线,离线,数据接收消息通知
2. 接收缓冲数据存储

复制代码

using DotNetty.Buffers;
using FusionDrive.DotNetty.Util;
using FusionDrive.DotNetty.Util.decode;
using FusionDrive.DotNetty.Util.socket;
using System.Net;
using System.Text;

namespace FusionDrive.DotNetty
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //Test_Server();

            Test_Client();

            Console.ReadLine();
        }

        static void Test_Server()
        {
            OHMDecode ohm = new OHMDecode();

            PipeManager.CheckLink();

            var server = new CommunicationServer();
            //server.Listen(8801);
            server.Listen(8801, isTcp: true, isUdp: true, udpTargetEndPoint: new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8802));
            server.OnPipeOnline += async (session) =>
            {
                Logger.Info($"-> {session.RemoteAddress?.ToString()} 上线"); 
            };
            server.OnPipeOffline += async (session) =>
            {
                Logger.Info($"<- {session.RemoteAddress?.ToString()} 离线");
            };
            server.OnPipeReceived += async (session, buffer) =>
            {
                // dtu模式
                if (session.IsFirstConnect)
                {
                    // 心跳包
                    session.IsFirstConnect = false;
                    var sessionId = Encoding.UTF8.GetString(buffer);
                    session.UpdateSessionId(sessionId);

                    // 下发TCP指令
                    SendData(session.SessionId);

                    // 清理
                    PipeManager.ClearByteBuffer(sessionId);
                }
                else
                {
                    ohm.ProtocolFormat(session.SessionId, session.ByteBuffer);
                }

                // 下发UDP指令
                SendData(session.SessionId, session.TargetEndPoint);
                SendData(session.SessionId, session.RemoteAddress);
            };
        }

        static void Test_Client()
        {
            AirDecode air = new AirDecode();

            var tcpClient = new TcpClient();
            tcpClient.Connect("127.0.0.1", 8801, autoConect:true);

            tcpClient.OnPipeOnline += async (session) =>
            {
                Logger.Info($"-> {session.RemoteAddress?.ToString()} 上线");
            };
            tcpClient.OnPipeOffline += async (session) =>
            {
                Logger.Info($"<- {session.RemoteAddress?.ToString()} 离线");
            };
            tcpClient.OnPipeReceived += async (session, buffer) =>
            {
                air.ProtocolFormat(session.SessionId, session.ByteBuffer);
                //
                //tcpClient.DisConnect();
            };
        }
        
        static void SendData(string sessionId, EndPoint endPoint = null)
        {
            IByteBuffer message = Unpooled.Buffer();
            message.WriteString("*TRG\r\n", Encoding.UTF8);
            var p = PipeManager.CheckSession(sessionId, out string errmsg);
            p.Send(message, endPoint);
        } 
    }
}

复制代码

复制代码

using DotNetty.Buffers;
using System.Text;

namespace FusionDrive.DotNetty.Util.decode
{
    public class AirDecode : NettyDecode
    {
        const byte HEAD = 02;

        public override void ProtocolFormat(string sessionId, IByteBuffer oldBuffer)
        { 
            try
            {
                if (IsHeadLost(oldBuffer, 1)) return ;// 掉包

                bool headok = false;

                // 垃圾包处理
                oldBuffer.MarkReaderIndex();

                while (oldBuffer.IsReadable())
                {
                    byte head1 = oldBuffer.ReadByte();// 1字节:标识头
                    if (HEAD == head1)
                    {
                        headok = true;
                        break;
                    }
                    else
                    {
                        oldBuffer.MarkReaderIndex();
                    }
                }

                if (!oldBuffer.IsReadable())
                {
                    if (!headok) return ; // 垃圾包
                }

                // 3字节:ascii长度
                if (IsBagLost(oldBuffer, 3)) return ;// 掉包
                byte[] arrlen = new byte[3]; oldBuffer.ReadBytes(arrlen);
                int len = Convert.ToInt32(Encoding.ASCII.GetString(arrlen));

                // n字节:数据包
                len = len + 2;// 包含结束帧及校验位
                if (IsBagLost(oldBuffer, len)) return ;// 掉包
                byte[] data = new byte[len]; oldBuffer.ReadBytes(data);

                Logger.Info($"AirDecode sessionId:{sessionId},data:{Common.ToHexString(data, data.Length, true)}");

                // 压力值
                string pressureValue = "999";
                var pressureFlg = Encoding.ASCII.GetString(new byte[] { data[55] });
                var pressureNumber = Encoding.ASCII.GetString(new byte[] {
                    data[56], data[57], data[58], data[59], data[60],
                    data[61], data[62], data[63], data[64], data[65] });// 可能为----------- 
                if (double.TryParse(pressureNumber, out double result1))
                {
                    pressureNumber = result1.ToString();
                    pressureValue = pressureFlg + pressureNumber;
                }
                Logger.Info($"AirDecode 压力值:{pressureValue}");

                if (oldBuffer.IsReadable())
                {
                    ProtocolFormat(sessionId, oldBuffer);// 处理粘包
                }
                else
                {
                    oldBuffer.Clear();// 包完整,清理
                } 
            }
            catch (Exception ex)
            {
                Logger.Error($"AirDecode 协议解析错误,sessionId:{sessionId}:{ex.Message}");
            } 
        }
    }
}

复制代码

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值