Networkcomms发送byte流

博客介绍了用海康视频接口抓图处理后返回大byte流并发送到前端实时显示的方法。使用开源networkcomms V3,测试单路发送,需分块发送大byte流。还提及另一种实现方式是将图片H265成视频直播,同时列出工程引用的第三方库及测试代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【场景】:用海康的视频接口连续抓图自己处理图像后返回一个大的byte流(大概有12万到16万字节),然后如何发送到前端实时显示图像?

【工具】:使用开源networkcomms V3.(VS2012,.NetFramework 4.5);

【说明】:这只是我用来测试的代码,测试用的单路发送,并未考虑多路发送的情况,其实也只需要修改调用代码即可;

这个本来用于文件传输的,文件也是byte流,抓图也是返回byte流,一样一样的;

【扩展】:这种场景,还有一种实现方式,把处理后的一张张的图片H265成视频然后直播,不过需要比较多的视频处理技术;

【工程引用第三方库】:

protobuf-net.dll

ProtobufSerializer.dll

NetworkCommsDotNet.dll

检出networkcomms的开源代码示列里面有直接拷贝过来即可

【测试代码】:

using MP.Entity;
using MP.GR;
using NetworkCommsDotNet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplicationForPictureChunkSend
{
    
    public partial class Form1 : Form
    {
        private long count = 0;
        private FileTransClient _client=new FileTransClient();

        public Form1()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);//双缓存防闪屏
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _client.Initial();

            HCPictureCapture.Instance.BMPBufferFull+=Instance_BMPBufferFull;//海康抓图事件
            HCPictureCapture.Instance.Start(new HCPresetLoginInfo() {IP="192.168.9.63",Port=8000,UserName="admin",Password="admin12345",Channel=1 });
            button1.Visible = false;
        }

        private void Instance_BMPBufferFull(MP.Entity.HCPresetLoginInfo info, byte[] buffer)
        {
            _client.SendFile(Guid.NewGuid().ToString(), buffer);//发送大的byte流
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FileTransSvr.Instance.Initial();
            //byte流接收完成,显示图片
            FileTransSvr.Instance.ReceiveCompleted += ((ConnectionInfo connInfo,Byte[] buffer) => 
            {
                count++;
                this.BeginInvoke(new Action(() => 
                {
                    using (MemoryStream ms = new MemoryStream(buffer))
                    {
                        if (null != this.BackgroundImage)
                        {
                            this.BackgroundImage.Dispose();
                        }
                        this.BackgroundImage = Image.FromStream(ms);
                        this.Text = count.ToString(); 
                    }
                }));
            });     
        }
    }
}

【接收服务】:

using NetworkCommsDotNet;
using NetworkCommsDotNet.Connections;
using NetworkCommsDotNet.Connections.TCP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplicationForPictureChunkSend
{
    public class FileTransSvr
    {
        private static object syncRoot = new object();
        private Dictionary<ConnectionInfo, Dictionary<long, SendInfo>> incomingDataInfoCache = new Dictionary<ConnectionInfo, Dictionary<long, SendInfo>>();
        private Dictionary<ConnectionInfo, Dictionary<long, byte[]>> incomingDataCache = new Dictionary<ConnectionInfo, Dictionary<long, byte[]>>();
        private Dictionary<ConnectionInfo, Dictionary<string, ReceivedFile>> receivedFilesDict = new Dictionary<ConnectionInfo, Dictionary<string, ReceivedFile>>();
        private static FileTransSvr _instance=null;

        public event Action<ConnectionInfo, byte[]> ReceiveCompleted;

        public FileTransSvr()
        {
            NetworkComms.AppendGlobalIncomingPacketHandler<byte[]>("PartialFileData",IncomingPartialFileData);
            NetworkComms.AppendGlobalIncomingPacketHandler<SendInfo>("PartialFileDataInfo",IncomingPartialFileDataInfo);
        }

        ~FileTransSvr()
        {
 
        }

        public static FileTransSvr Instance
        {
            get
            {
                if (null == _instance)
                {
                    lock (syncRoot)
                    {
                        if (null == _instance)
                        {
                            _instance = new FileTransSvr();
                        }
                    }
                }
                return _instance;
            }
        }
        /// <summary>
        /// 接收到的文件数据
        /// </summary>
        /// <param name="packetHeader"></param>
        /// <param name="connection"></param>
        /// <param name="data"></param>
        private void IncomingPartialFileData(PacketHeader packetHeader, NetworkCommsDotNet.Connections.Connection connection, byte[] data)
        {
            try
            {
                //SendInfo info = null;
                //ReceivedFile file = null;

                lock (syncRoot)
                {
                    long sequenceNumber = packetHeader.GetOption(PacketHeaderLongItems.PacketSequenceNumber);

                    if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
                        incomingDataCache.Add(connection.ConnectionInfo, new Dictionary<long, byte[]>());
                    incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber, data);

                    
                //    if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo) &&
                //        incomingDataInfoCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
                //    {
                //        info = incomingDataInfoCache[connection.ConnectionInfo][sequenceNumber];
                //        incomingDataInfoCache[connection.ConnectionInfo].Remove(sequenceNumber);
                //        if (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                //            receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>());
                //        //如果当前收到字节数据,还没有对应的ReceivedFile类,则创建一个
                //        if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
                //        {
                //            receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes));
                //        }
                //        file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
                //    }
                //    else
                //    {
                //        if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
                //            incomingDataCache.Add(connection.ConnectionInfo,new Dictionary<long,byte[]>());
                //        incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber,data);
                //    }
              

                //if (info != null && file != null && !file.IsCompleted)
                //{
                //    file.AddData(info.BytesStart,0,data.Length,data);
                //    if (file.IsCompleted)
                //    {
                //        if (null != ReceiveCompleted)
                //        {
                //            ReceiveCompleted(connection.ConnectionInfo,file.FileBuffer);
                //        }
                //        if (incomingDataCache.ContainsKey(connection.ConnectionInfo))
                //            incomingDataCache[connection.ConnectionInfo].Clear();
                //        if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
                //            incomingDataInfoCache[connection.ConnectionInfo].Clear();
                //        if (receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                //            receivedFilesDict[connection.ConnectionInfo].Clear();
                //    }
                //    file = null;
                //    data = null;
                //}
                //else if (info == null ^ file == null)
                //{
                //    throw new Exception("Either both are null or both are set. Info is " + (info == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
                //}
                }
            }
            catch(Exception ex)
            {
                NetworkComms.Logger.Fatal("IncomingPartialFileDataError",ex);
            }
        }

        /// <summary>
        /// 接收到的文件信息
        /// </summary>
        /// <param name="packetHeader"></param>
        /// <param name="connection"></param>
        /// <param name="incomingObject"></param>
        private void IncomingPartialFileDataInfo(PacketHeader packetHeader, NetworkCommsDotNet.Connections.Connection connection, SendInfo info)
        {
            try
            {
                byte[] data = null;
                ReceivedFile file = null;
                lock (syncRoot)
                {
                    long sequenceNumber = info.PacketSequenceNumber;
                    if (incomingDataCache.ContainsKey(connection.ConnectionInfo) && 
                        incomingDataCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
                    {
                        //如果当前文件信息类对应的文件字节部分已经存在
                        data = incomingDataCache[connection.ConnectionInfo][sequenceNumber];
                        incomingDataCache[connection.ConnectionInfo].Remove(sequenceNumber);
                        if (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                            receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>());
                        if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
                        {
                            receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes));
                        }
                        file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
                    }
                    else
                    {
                        if (!incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataInfoCache.Add(connection.ConnectionInfo, new Dictionary<long, SendInfo>());
                        incomingDataInfoCache[connection.ConnectionInfo].Add(sequenceNumber, info);
                    }
               

                if (data != null && file != null && !file.IsCompleted)
                {
                    file.AddData(info.BytesStart, 0, data.Length, data);
                    if (file.IsCompleted)
                    {
                        if (null != ReceiveCompleted)
                        {
                            ReceiveCompleted(connection.ConnectionInfo, file.FileBuffer);
                        }
                        if (incomingDataCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataCache[connection.ConnectionInfo].Clear();
                        if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataInfoCache[connection.ConnectionInfo].Clear();
                        if (receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                            receivedFilesDict[connection.ConnectionInfo].Clear();
                    }
                    file = null;
                    data = null;
                }
                else if (data == null ^ file == null)
                    throw new Exception("Either both are null or both are set. Data is " + (data == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
                }
            }
            catch(Exception ex)
            {
                NetworkComms.Logger.Fatal("IncomingPartialFileDataInfoError",ex);
            }
        }


        public void Initial()
        {
            IPEndPoint thePoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
            Connection.StartListening(ConnectionType.TCP,thePoint,false);
        }


    }
}

【发送端】:

分块发送,sendChunkSizeBytes可以自己改

using NetworkCommsDotNet;
using NetworkCommsDotNet.Connections.TCP;
using NetworkCommsDotNet.DPSBase;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WindowsFormsApplicationForPictureChunkSend
{
    public class FileTransClient
    {
        private TCPConnection newTCPConnection = null;
        private SendReceiveOptions customOptions = new SendReceiveOptions<ProtobufSerializer>();

        public Boolean Initial()
        {
            try
            {
                ConnectionInfo connInfo = new ConnectionInfo("127.0.0.1", 9999);
                newTCPConnection = TCPConnection.GetConnection(connInfo);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public void SendFile(string fileName,Byte[] buffer)
        {
            try
            {
                MemoryStream stream = new MemoryStream(buffer);
                NetworkCommsDotNet.Tools.StreamTools.ThreadSafeStream safeStream = new NetworkCommsDotNet.Tools.StreamTools.ThreadSafeStream(stream);
                long sendChunkSizeBytes = 40960;
                long totalBytesSent = 0;
                do
                {
                    long bytesToSend = (totalBytesSent + sendChunkSizeBytes) < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent;
                    NetworkCommsDotNet.Tools.StreamTools.StreamSendWrapper streamWrapper = new NetworkCommsDotNet.Tools.StreamTools.StreamSendWrapper(safeStream, totalBytesSent, bytesToSend);
                    long packetSequenceNumber;
                    newTCPConnection.SendObject("PartialFileData", streamWrapper, customOptions, out packetSequenceNumber);
                    newTCPConnection.SendObject("PartialFileDataInfo", new SendInfo(fileName, stream.Length, totalBytesSent, packetSequenceNumber), customOptions);
                    totalBytesSent += bytesToSend;
                    Thread.Sleep(0);
                }
                while (totalBytesSent < stream.Length);
                Array.Clear(buffer,0,buffer.Length);
            }
            catch(CommunicationException)
            { }
        }
    }
}

【契约类】:

using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplicationForPictureChunkSend
{
    [ProtoContract]
    public class SendInfo
    {
        [ProtoMember(1)]
        public string Filename { get; private set; }

        [ProtoMember(2)]
        public long BytesStart { get; private set; }

        [ProtoMember(3)]
        public long TotalBytes { get; private set; }

        [ProtoMember(4)]
        public long PacketSequenceNumber { get; private set; }

        private SendInfo() { }

        public SendInfo(string filename, long totalBytes, long bytesStart, long packetSequenceNumber)
        {
            this.Filename = filename;
            this.TotalBytes = totalBytes;
            this.BytesStart = bytesStart;
            this.PacketSequenceNumber = packetSequenceNumber;
        }

    }
}

【接收文件类】:

using NetworkCommsDotNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplicationForPictureChunkSend
{
    public class ReceivedFile
    {
        private byte[] buffer;
        private long receivedLength = 0;

        public string FileName;
        public ConnectionInfo connInfo;
        public long TotalBytes;
        

        public void AddData(long bytesStart, long dataStart, long dataLength, byte[] data)
        {
            Array.Copy(data,0,buffer,bytesStart,dataLength);
            receivedLength += dataLength;
        }

        public ReceivedFile(string fileName,ConnectionInfo connInfo,long totalBytes)
        {
            this.FileName = fileName;
            this.connInfo = connInfo;
            this.TotalBytes = totalBytes;
            this.receivedLength = 0;
            buffer=new byte[totalBytes];
        }

        public Boolean IsCompleted
        {
            get
            {
                return receivedLength==TotalBytes;
            }
        }

        public byte[] FileBuffer
        {
            get
            {
                return buffer;
            }
        }
    }
}

写在最后,自己在c#网络通讯方面一直喜欢用networkcomms,成熟的通讯库,一直用很久没问题,然后如何稳定高效的发送大的byte流折腾了2天,由服务端发送到前端开始直接sendobject行不通不稳定,这么大的byte流只能分块发送,那如何分块发送就是上面代码主要要实现的功能,把自己2天的成果贴出来希望能帮助到有需要的你们;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值