基于superSocket——AForge的UDP传输摄像案例——服务端

服务端

业务逻辑
基于superSocket——AForge的UDP传输摄像案例——服务端_第1张图片

基于superSocket——AForge的UDP传输摄像案例——服务端_第2张图片

这里打个比方,有个陌生人(摄像客户端)一直在敲你家的门,但是你没开,等到你爸(Web客户端)给你一个命令开门,你就把门打开,让陌生人进来,并进行核对信息(信息匹配),原来是隔壁老王,给你带了好吃的(UDP数据),你于是就把吃的留下来了。

准备操作:
用superSocket接受客户端发来的UDP数据,
superSocket开源框架网址:https://github.com/kerryjiang/SuperSocket。
在NuGet中添加:SuperSocket,SuperSocket.Engine,SuperSocket.ProtoBase
用FFMPEG、VFW进行录入视频。
.新建UdpTestSession继承AppSession,用于建立会话;

using SuperSocket.SocketBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SuperSocket.SocketBase.Protocol;

namespace Receive.udp
{
    public class UdpTestSession : AppSession
    {
        protected override void OnSessionClosed(CloseReason reason)
        {
            base.OnSessionClosed(reason);
        }

        protected override void OnSessionStarted()
        {
            Console.WriteLine("start Session");
            base.OnSessionStarted();
        }

        protected override void HandleUnknownRequest(MyUdpRequestInfo requestInfo)
        {
            Console.WriteLine("unKnownRequest");
            base.HandleUnknownRequest(requestInfo); 
        }
    }
}
**②**.新建MyUdpRequestInfo,用于封装会话信息与传输的数据信息。
using Receive.udp;
using SuperSocket.ProtoBase;
using SuperSocket.SocketBase.Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Receive.udp
{

    public class MyUdpRequestInfo : UdpRequestInfo, IPackageInfo
    {
        public MyUdpRequestInfo(string key, string sessionID)
            : base(key, sessionID)
        {

        }


        public byte[] Body { get; set; }


    }

}

③.新建MyReceiveFilter,用于处理(过滤)传输的数据,封装成MyUdpRequestInfo。其中这步中,我将客户端分为web客户端摄像设备客户端,web 发送的数据类型 密钥+设备号+录像时间+结束标志,录像设备发送的数据 密钥+设备号+照片+结束标志。
其中密钥与设备号可以设置固定长度,不足补休止符。(见下辅助类)

using System.Linq;


namespace Receive.udp
{

   public class MyReceiveFilter : SuperSocket.SocketBase.Protocol.IReceiveFilter
    {
        public int LeftBufferSize
        {
            get { return 0; }
        }

        public SuperSocket.SocketBase.Protocol.IReceiveFilter NextReceiveFilter
        {
            get { return this; }
        }

        public SuperSocket.SocketBase.Protocol.FilterState State
        {
            get; private set;
        }
        /// 
        /// 过滤器接受信息 解析byte[]
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public MyUdpRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest)
        {
            var endFlag = HandleUdpUtils.getEndFlagStr(readBuffer, length);
            if (!endFlag.Equals(HandleUdpUtils.IMG_END_FLAG)||!endFlag.Equals(HandleUdpUtils.WEB_END_FLAG)) //判断结束标志
            {
                int endFlagSize = 2;
                string privateKey =  HandleUdpUtils.getPrivateKey(readBuffer);
                string sesssionId = HandleUdpUtils.getMachineCode(readBuffer); //machineCode 当成sessionId
                rest = 0;
                byte[] body = readBuffer.Skip(HandleUdpUtils.MACHINE_CODE_LENGTH).Take(length - HandleUdpUtils.MACHINE_CODE_LENGTH - endFlagSize).ToArray();
                return new MyUdpRequestInfo(privateKey, sesssionId) { Body = body };
            }else
            {
                rest = 0;
                return null;
            }
        }

        public void Reset()
        {

        }
    }
}

④.新建UdpAppServer,集中处理传递的数据,根据不同的客户端请求,进行不一样的操作。web设备的请求则开启摄像,摄像设备则进行视频的录入。

using SuperSocket.SocketBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketBase.Protocol;
using SuperSocket.ProtoBase;

using SuperSocket.SocketBase.Logging;
using System.Collections.Concurrent;
using log4net;
using System.IO;
using System.Drawing;

namespace Receive.udp
{
    class UdpAppServer : AppServer
    {
        public static readonly log4net.ILog LOG = log4net.LogManager.GetLogger("VideoSocket");
        public static readonly string PATH = "D:\\ReadVersionD\\video";
        public static Dictionary<string, string> PRIVATE_KEY = new Dictionary<string, string>(); //deviceCode->key
        public static readonly int MIN_IMG_SIZE = 100;
        public UdpAppServer()
              : base(new DefaultReceiveFilterFactory())
        {

        }

        public static ConcurrentDictionary<string, VideoPackageModel> FILE_WRITERS = new ConcurrentDictionary<string, VideoPackageModel>(); //读取文件

        protected override void OnSystemMessageReceived(string messageType, object messageData)
        {
            Console.WriteLine(messageType);
            base.OnSystemMessageReceived(messageType, messageData);
        }


        /// 
        /// 过滤器后 进入该方法
        /// 
        /// 
        /// 
        protected override void ExecuteCommand(UdpTestSession session, MyUdpRequestInfo requestInfo)
        {
            if (requestInfo.Key.Equals(HandleUdpUtils.WEB_SESSION_KEY))
            {
                handleWebUdpSocket(requestInfo, session);
                return;
            }
            string privateKey = "";
            if (PRIVATE_KEY.TryGetValue(requestInfo.SessionID, out privateKey) && privateKey.Equals(requestInfo.Key))
            {
                handleDeviceInfo(requestInfo);
                session.Send("receive success");
            }
            else
            {
                LOG.Error(requestInfo.SessionID + "|不存在该设备");
            }

        }





        #region WEB

        /// 
        /// 发送socket 到web客户端
        /// 
        /// 
        /// 
        private void handleWebUdpSocket(MyUdpRequestInfo requestInfo, UdpTestSession session)
        {
            switch (handleWebCommand(requestInfo))
            {
                case 1:
                    session.Send("addOK");
                    break;
                case 0:
                    session.Send("dataError");
                    break;
                case -1:
                    session.Send("alreadyExit");
                    break;
            }      
        }


        /// 
        /// body的组成 : 5|DateType
        /// 
        /// 
        private int handleWebCommand(MyUdpRequestInfo requestInfo)
        {
            var startTime = DateTime.Now;
            DateTime endTime;
            int seconds = HandleUdpUtils.getLasingTime(requestInfo.Body,out startTime,out endTime);
            if (seconds == 0)
            {
                LOG.Error(requestInfo.SessionID + "|获取录像持续时间失败!");
                return 0;
            }          
            string privateKey = "";
            PRIVATE_KEY.TryGetValue(requestInfo.SessionID,out privateKey);



            if (!addVideoPackageModel(requestInfo.SessionID,privateKey,startTime,endTime,seconds))
            {
                Console.WriteLine(requestInfo.SessionID + "|添加失败!");
                LOG.Error(requestInfo.SessionID + "|添加失败!");//TODO 存在更新的操作
                return -1;
            }
            else {
                Console.WriteLine(requestInfo.SessionID + "|添加成功!");
                LOG.Info(requestInfo.SessionID + "|添加成功!");
                return 1;
            }        
        }

        /// 
        /// 添加VideoPackageModel
        /// 
        /// 
        /// 
        /// 
        private bool addVideoPackageModel(string sessionId, string privateKey,DateTime startTime,DateTime endTime ,int seconds)
        {
            VideoPackageModel oldModel;

            if(FILE_WRITERS.TryGetValue(sessionId,out oldModel))
            {
                if (oldModel.Disposed) //可能没删除
                {
                    FILE_WRITERS.TryRemove(sessionId,out oldModel);
                    VideoPackageModel model = new VideoPackageModel(privateKey, sessionId, startTime, endTime, seconds, PATH); //避免计时器出错
                    return FILE_WRITERS.TryAdd(sessionId, model);
                }
                return false;
            }else
            {
                VideoPackageModel model = new VideoPackageModel(privateKey, sessionId, startTime, endTime, seconds, PATH);
                return FILE_WRITERS.TryAdd(sessionId, model);
            }
        }


        #endregion



        #region 录像设备
        /// 
        /// 处理设备发来的视频
        /// 
        /// 
        private void handleDeviceInfo(MyUdpRequestInfo requestInfo)
        {
            VideoPackageModel videoModel = getVideoPackage(requestInfo.SessionID);

            if (videoModel == null || requestInfo.Body.Length< MIN_IMG_SIZE) {
                return;
            }

            MemoryStream stream = null;
            stream = new MemoryStream(requestInfo.Body);
            Bitmap img = null;
            try
            {
                img = HandleUdpUtils .BytesToBitmap(requestInfo.Body) ;
                // img.Save("D:\\JGHPCXReadVersionD\\video\\001\\test.jpg");
                 videoModel.wirteVideo(img);

            }
            catch (Exception e)
            {
                LOG.Error(requestInfo.SessionID+"|"+e.ToString());
                Console.WriteLine(requestInfo.SessionID + "|" + e.ToString());
            }finally
            {
                if(stream!=null)
                stream.Close();
                if(img!=null)
                img.Dispose();
            } 
        }

        /// 
        /// getVideoPackage
        /// 
        /// 
        /// 
        private VideoPackageModel getVideoPackage(string sessionId)
        {
            VideoPackageModel video;
            if(FILE_WRITERS.TryGetValue(sessionId, out video))
            {
                LOG.Error(sessionId + "|设备未开启录像");
            }
            return video;
        }


        #endregion


    }
}

辅助类——VideoPackageModel:用于封装Video数据与操作。

using Accord.Video.FFMPEG;
using Accord.Video.VFW;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Timers;

namespace Receive.udp
{
    public  class VideoPackageModel: IDisposable
    {
        //校验码
        public string PrivateKey { get; }
        //机器码
        public string SessionId { get;}
        //录制开始时间
        public DateTime StartTime { get; }
        //录制结束时间
        public DateTime EndTime { get; }
        //视频路径
        public string VideoPath { get; }
        //计时器
        private System.Timers.Timer Timer = new System.Timers.Timer();
        //视频写入(32位的FFMPEG)
       // private VideoFileWriter Writer = new VideoFileWriter();

        //是否释放资源

        public bool  Disposed = false;
        //avi读取
        private AVIWriter aviWriter = new AVIWriter("wmv3");

        //VideoFileWriter 所需的时间戳
        DateTime _firstFrameTime;

        public VideoPackageModel(string privateKey,string sessionId,DateTime startTime,DateTime endTime,int second,string path)
        {
            SessionId = sessionId;
            PrivateKey = privateKey;
            StartTime = startTime;
            EndTime = endTime;
            _firstFrameTime = DateTime.Now;
            int width = 848;    //录制视频的宽度
            int height = 480;   //录制视频的高度
            int fps = 20;
            path = getTimeStamp(path);


            aviWriter.Open(path, Convert.ToInt32( width), Convert.ToInt32( height));
         //  Writer.Open(path, width, height, fps, VideoCodec.Default);

            int lastTime = second * 1000;
            Console.WriteLine("持续时间"+lastTime);

            Timer.Elapsed += new System.Timers.ElapsedEventHandler(endTimeEvent);   //到达时间的时候执行事件;
            Timer.AutoReset = false;   //设置是执行一次(false)还是一直执行(true);
            Timer.Interval = lastTime;//设置定时间隔(毫秒为单位)
            Timer.Enabled = true;

        }

        /// 
        /// video 存储路径
        /// 
        /// 
        /// 
        private string  getTimeStamp(string path) {
            System.DateTime originTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            long timeStamp = (long)(StartTime - originTime).TotalMilliseconds;
            string filePath = path + "\\" + SessionId;
            if (!Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
            }
            return filePath + "\\" + timeStamp + ".avi";
        }



        /// 
        /// 结束资源
        /// 
        /// 
        /// 
        private  void endTimeEvent(object sender, ElapsedEventArgs e)
        {
            using (this)
            {

                VideoPackageModel videoModel;
                UdpAppServer.FILE_WRITERS.TryRemove(SessionId, out videoModel);
                Console.WriteLine("endWriters");
            }
        }

        /// 
        /// 写入视频
        /// 
        /// 
        public void wirteVideo(Bitmap  img)
        {
            try
            {
                lock (aviWriter)
                {
                    if (aviWriter != null)
                        aviWriter.AddFrame(img);
                }


            //    if(Writer != null && Writer.IsOpen)
            //    {
            //        lock (Writer)
            //        {
            //            if (_firstFrameTime != null)
            //            {

            //                Writer.WriteVideoFrame(img, TimeSpan.FromMilliseconds(DateTime.Now.ToUniversalTime().Subtract(
            //new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
            //).TotalMilliseconds - _firstFrameTime.ToUniversalTime().Subtract(
            //new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
            //).TotalMilliseconds));
            //            }
            //            else
            //            {
            //                Writer.WriteVideoFrame(img);
            //                _firstFrameTime = DateTime.Now;
            //            }
            //        }
            //    }

            }
            catch (Exception e)
            {
                _firstFrameTime = DateTime.Now;
                Console.WriteLine(e.ToString());
            }

        }

        #region Dispose 操作

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
      {
       if (!Disposed)
       {
           if (disposing)
            {
                    if (aviWriter != null)
                    {
                        try
                        {
                            aviWriter.Close();
                            aviWriter.Dispose();
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);

                        }
                        finally
                        {
                            aviWriter = null;
                        }


                    }
                    if(Timer!=null)
                    {
                        Timer.Dispose();
                        Timer = null;
                    }
           }
                //处理非托管
                Disposed = true;
        }
     }




     ~VideoPackageModel()
     {
        Dispose(false);
     }


        #endregion

    }
}

辅助类——字节数组操作类。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace Receive.udp
{
    //handle byte 
    public class HandleUdpUtils
    {
        public static readonly string IMG_END_FLAG = "##"; //设备传来的结束标志

        public static readonly string WEB_END_FLAG = "**"; //WEB传来的结束标志

        public static readonly string WEB_SESSION_KEY = "f21c2a0689443179082e02f8f44079";

        public static readonly int KEY_LENGTH = 40;

        public static readonly int MACHINE_CODE_LENGTH = 80;

        public enum TimeTypeEnum { Day,Hour, Minute,Second }


        /// 
        /// 读取byte[]并转化为图片
        /// 
        /// byte[]
        /// Image
        public static Bitmap BytesToBitmap(byte[] Bytes)
        {
            MemoryStream stream = null;
            try
            {
                stream = new MemoryStream(Bytes);
                return new Bitmap((Image)new Bitmap(stream));
            }
            catch (ArgumentNullException ex)
            {
                throw ex;
            }
            catch (ArgumentException ex)
            {
                throw ex;
            }
            finally
            {
                stream.Close();
            }
        }

        /// 
        /// 得到传输来的结束标志
        /// 
        /// 
        /// 
        /// 
        public static string getEndFlagStr(byte[] bytes, int length)
        {
            int begin = length - 2;
            int size = 2;
            string endFlag = Encoding.ASCII.GetString(bytes, begin, size);
            return String.IsNullOrEmpty(endFlag)?"":endFlag;
        }

        /// 
        /// 根据标志判断客户端类型
        /// 
        /// 
        /// 
        /// 
        public static int getClientType(byte[] bytes, int length)
        {
          var endFlagStr=  getEndFlagStr(bytes ,length);
            if (endFlagStr.Equals(WEB_END_FLAG)) //web
                return 1;
            else if (endFlagStr.Equals(IMG_END_FLAG)) //device
                return 2;
            else
                return 0;

        }

        /// 
        /// 长度为40的密钥
        /// 
        /// 
        /// 
        public static string getPrivateKey(byte[] bytes)
        {
            string privateKey = "";
            if (bytes.Length <= KEY_LENGTH)
                return privateKey;
            privateKey = Encoding.ASCII.GetString(bytes, 0, KEY_LENGTH).TrimEnd('\0'); //取掉休止符

            return privateKey;
        }

       /// 
       /// 得到机器码
       /// 
       /// 
       /// 
        public static string getMachineCode(byte[] bytes)
        {
            string machineCode = "";
            if (bytes.Length <= MACHINE_CODE_LENGTH)
                return machineCode;
            machineCode = Encoding.ASCII.GetString(bytes, KEY_LENGTH, MACHINE_CODE_LENGTH-KEY_LENGTH).TrimEnd
                ('\0');

            return machineCode;
        }


        /// 
        /// 密钥构造成字节数组 (客户端)
        /// 
        /// 
        /// 
        public static byte[] InitPrivateKeyBytes(string privateKey)
        {
            byte[] fixedBytes = new byte[40];
            if (String.IsNullOrEmpty(privateKey))
                return fixedBytes;
            var bytes = Encoding.Default.GetBytes(privateKey);

            for (int i = 0; i < bytes.Length; i++)
                fixedBytes[i] = bytes[i];


            return fixedBytes;

        }


        /// 
        /// web取截止时间
        /// 
        /// 
        /// 
        /// 
        public static bool StrToTime(byte[] body, out DateTime endTime)
        {
            string str = Encoding.Default.GetString(body);
            endTime = DateTime.Now;
            try
            {
                DateTime dateTime = Convert.ToDateTime(str);
                endTime = dateTime;
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        /// 
        /// 得到计时时间
        /// 
        /// 
        /// 
        /// 
        /// 
        public static int getLasingTime(byte[] body,out DateTime startTime,out DateTime endTime)
        {

            string str = Encoding.Default.GetString(body);
            int index = str.IndexOf("|");
            startTime = DateTime.Now;
            endTime = DateTime.Now;
            if (index == -1)
                return 0;                
            var timeType = str.Substring(index+1); //5|Second
            var num = 0;
            try
            {
                num = Convert.ToInt32(str.Substring(0, index));
            }
            catch (Exception)
            {
                 return 0;
            }

            switch (timeType)
            {
                case nameof(TimeTypeEnum.Day) :
                    num *= 12 * 60 * 60;
                    break;
                case nameof(TimeTypeEnum.Hour):
                    num *= 60 * 60;
                    break;
                case nameof(TimeTypeEnum.Minute):
                    num *= 60;
                    break;
                case nameof(TimeTypeEnum.Second):

                    break;
                default:
                    num = 0;
                    break;
            }
            endTime = startTime.AddSeconds(num);
            return num;
        }
    }
}

主函数:

using Receive.udp;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UdpSocketServer
{
    class Program
    {
        private static IServerConfig SERVER_CONFIG;
        private static IRootConfig ROOT_CONFIG;
        public static IServerConfig DefaultServerConfig
        {
            get
            {
                return new ServerConfig
                {
                    Ip = "127.0.0.1",
                    LogCommand = true,
                    MaxConnectionNumber = 1000,
                    Mode = SocketMode.Udp,
                    Name = "Udp Test Socket Server",
                    Port = 6602,
                    ClearIdleSession = true,
                    ClearIdleSessionInterval = 1,
                    IdleSessionTimeOut = 5,
                    SendingQueueSize = 100,
                    ReceiveBufferSize = 50000
                };
            }
        }


        static void Main(string[] args)
        {
            initData();
            SERVER_CONFIG = DefaultServerConfig;
            ROOT_CONFIG = new RootConfig();
            var testServer = new UdpAppServer();
            testServer.Setup(ROOT_CONFIG, SERVER_CONFIG);
            testServer.Start();
            while (true)
            {

            }
        }

        private static void initData()
        {
            UdpAppServer.PRIVATE_KEY.Add("webSession", "f21c2a0689443179082e02f8f44079");
            UdpAppServer.PRIVATE_KEY.Add("001", "key001");
            UdpAppServer.PRIVATE_KEY.Add("002", "key002");
            UdpAppServer.PRIVATE_KEY.Add("003", "key003");
        }
    }
}

下载Demo地址:
https://download.csdn.net/download/a748448660/10559647

你可能感兴趣的:(C#)