c#实现Udp通信(四)--UPD大数据量接收(异步接收)

公众号“毛线杂货铺”,订阅收费文章可淘bao店铺ID:122344852

之前困恼了很久的问题,终于解决了,项目要求使用Upd传输,128000的采样率 ,每次一个通道2000个数据,一个数据用3个字节传输,共8通道,最后两字节验证码,最后计算转成double值,用曲线显示并保存文件。
则:
128000/2000=62(次)
1/62≈0.016(s)=16(ms)
200038+2=48002(字节)
相当于循环16ms传输48002个字节。
在这里插入图片描述

在之前的项目中,没有解决,让c++的同事写的插件,空闲时还是要自己研究下哈,但是资料真的好难找,最后自己摸索,实现了,下面把帮助类贴上,供大家一起讨论学习。

public  class BigDataUDPServerHelper
{
    private Socket _server;
    private IPAddress Ip;//本机设备ip
    public int Port;//本机设备端口号
    private EndPoint _remote;//对方设备Ip及端口

    public int PagLength = 1472;//从服务读取的数据量,因为BeginReceiveFrom每次都是提前设置下一次接受,要注意如何传递
    int buffIndex = 0;//使用的缓存数组索引
    public byte[][] recvBuff0;//缓存数组
    readonly int MAX_BUFFLEN = 32;//最大缓存数组

    private int _readLen = 6002; //从服务读取的数据量,因为BeginReceiveFrom每次都是提前设置下一次接受,要注意如何传递
    public BigDataUDPServerHelper(string clientIpString,int clientPort,int size)
    {
        if (!IPAddress.TryParse(GetLocalIP(), out Ip))
        {
            MessageBox.Show("获取本机信息错误!");
            return ;
        }
        if (!int.TryParse(GetLocalValidPort(), out Port))
        {
            MessageBox.Show("本机没有可用的端口!");
            return ;
        }
        var clientIp= IPAddress.Parse(clientIpString);
        _remote = new IPEndPoint(clientIp, clientPort);
        PagLength = size;
        Init();
    }
   /// <summary>
   /// 缓存清零,初始化
   /// </summary>
    public void BuffInit()
    {
        recvBuff0 = new byte[MAX_BUFFLEN][];
        for (int i = 0; i < MAX_BUFFLEN; i++)
        {
            recvBuff0[i] = new byte[50000];
        }
    }
    /// <summary>
    /// 初始化服务
    /// </summary>
    public void Init()
    {
        BuffInit();
        _server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        _server.Bind(new IPEndPoint(Ip, Port));//绑定端口号和IP
        //接受回调 ReceiveMsg是回调函数
        _server.BeginReceiveFrom(recvBuff0[buffIndex], 0, PagLength, 0, ref _remote, ReceiveMsg, null);
    }
    
    #region 接收
    /// <summary>
    /// 设置接收长度
    /// </summary>
    /// <param name="size"></param>
    public void SetBufferSize(int size)
    {
        PagLength = size;
    }
    public int realBytesRecv = 0;//实际已接收字节数
    public event Action<byte[]> ReceiveEvent;//将数据转到界面
    public void ReceiveMsg(IAsyncResult ar)
    {
        //能进入这个事件,表明缓存中已存好本次的数据,不是通过BeginReceiveFrom来读取,BeginReceiveFrom是准备下次的
        int nRead = _server.EndReceive(ar);//获取传递过来的字节数。
        if (nRead <= 0|| realBytesRecv >= PagLength)
        {
            realBytesRecv = 0;
            var Inforstr = "Socket rec33recv Error code:" + "\r\n";//出错
            return;
        }
        realBytesRecv += nRead;
        if (realBytesRecv >= PagLength)//接受完整,大数据字节包过来受网络限制,最大传递1472个字节,所以需要拼包
        {
            realBytesRecv = 0;
            buffIndex++;
            if (buffIndex > (MAX_BUFFLEN - 1))
                buffIndex = 0;
            Thread t1 = new Thread(ThreadReceiveEvent);
            t1.Start();
        }
        //必须加,为下次接受回调做准备
        _server.BeginReceiveFrom(recvBuff0[buffIndex], realBytesRecv, _readLen, 0, ref _remote, ReceiveMsg, null);
        
    }
    /// <summary>
    /// 获取网络字节包
    /// </summary>
    public void ThreadReceiveEvent()
    {
        var buffer1 = new byte[PagLength];
        var indx = buffIndex - 1;
        if (indx == -1)
            indx = MAX_BUFFLEN-1;
        Array.Copy(recvBuff0[indx], buffer1, PagLength);//复制接收数据,减去可能多余的数组
        ReceiveEvent.Invoke(buffer1);
    }
    #endregion
    #region 发送
    public bool SendMsg(byte[] value)
    {
        var res = _server.SendTo(value, _remote);//会触发BeginReceiveFrom事件,好像是因为EndReceive
        if (res < 0)
        {
            return false;
        }
        return true;
    }
    #endregion
    /// <summary>
    ///  // 获得本机局域网IP地址 
    /// </summary>
    /// <returns></returns>
    private string GetLocalIP()
    {
        IPAddress[] AddressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
        if (AddressList.Length < 1)
        {
            return "";
        }
        return AddressList[0].ToString();
    }
    /// <summary>
    /// 获取本地有效的端口号
    /// </summary>
    /// <returns></returns>
    private string GetLocalValidPort()
    {
        IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
        //返回本地计算机上的所有Tcp监听程序    
        var ipsTcp = properties.GetActiveTcpListeners();
        //返回本地计算机上的所有UDP监听程序     
        var ipsUdp = properties.GetActiveUdpListeners();
        for (int i = 1024; i < 5000; i++)
        {
            foreach (var ip in ipsTcp)
            {
                if (ip.Port == i) continue;
                foreach (var ip2 in ipsUdp)
                {
                    if (ip2.Port != i)
                    {
                        return i.ToString();
                    }
                }
            }
            if (i == 4999)
            {
                return "";
            }

        }
        return "";
    }
}

使用方法:
连接:

BigDataUDPServerHelper uDPServerHelper = new BigDataUDPServerHelper("ip地址",int.Parse("端口号"), int.Parse("接收字节长度"));
uDPServerHelper.ReceiveEvent += Ss_ReceiveEvent;//接收字节事件
richTextBox1.AppendText("连接成功\n");

修改接收字节长度:

uDPServerHelper.SetBufferSize(int.Parse(textBox1.Text));

发送字节:

uDPServerHelper.SetBufferSize(6002);
byte[] code = new byte[] { 0x55, 0xaa,0x01,0x80,0x2b,0xf9,0x2f,0xd3,0xa1,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf3 };
var result = uDPServerHelper.SendMsg(code);
var str = result ? "发送成功\n" : "发送失败\n";
richTextBox1.AppendText(str);

接收字节:
在连接的时候,我们绑定了接收字节事件,此事件会在接收完整字节后触发,或者可找到帮助类中对应的位置修改传递方式。
考虑到高速大数据量,我在该接收函数中又添加了三维数组用于缓存,截图如下,因个人解析不同,故只用于参考:
ps:注意此处不能占用过多时间,会导致接收不完整。
在这里插入图片描述

写在最后:
和之前的upd通信相比,本次采用了异步通信的方式,但是这样的方式并不能说就实现了大数据量接收,因为数据到了,保存、解析、计算、使用都会占用时间,可能影响下次的接收保存。
所以我采用了二维数组缓存(二维数组不是一个整体的内存空间,是每个一维数组一个地址,所以不会造成读写使用一个对象 ),这样有32个一维数组可使用,将数据按条件存满一个第一个一维数组后,我使用下个一维数组进行保存,同时在线程中将第一个一维数组去进行解析、计算、使用,互不干涉。
同时在界面方面,也采用了多维数组进行缓存。
只有在每个环节都减少对象占用的等待时间,数据接收才能完整呈现。
最后,本有尝试添加一个接口,用于封装解析,但是发觉数据采集一段时间后出错,所以在不了解性能的情况下,尽量少一点封装较好。

  • 6
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yyuanyuxin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值