美文网首页
untiy 使用UDP发送图片,并将接收到的图片保存到本地

untiy 使用UDP发送图片,并将接收到的图片保存到本地

作者: 带着面包去流浪 | 来源:发表于2019-03-05 10:27 被阅读0次

    前言:

    UDP客户端作为图片发送方,UDP服务端作为图片接收方。

    实现功能:

    在发送方鼠标左键点击game视图,将本地图片发送到接收方,接收方将收到的图片保存到本地,并将接收到的图片在UI上展示几秒钟。

    发送方脚本挂载方式:

    udpclient.png

    接收方脚本挂载方式:

    udpserver.png

    客户端代码:

    NewUDPClient:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    public class NewUDPClient : MonoBehaviour {
    
        public static NewUDPClient instance;
        //服务端的IP
        string UDPClientIP;
        //目标socket 
        Socket socket;
        //服务端 
        EndPoint serverEnd;
        //服务端端口 
        IPEndPoint ipEnd;
        //接收的字符串 
        string recvStr;
        //接收的数据,必须为字节 
        byte[] recvData;
        //发送的数据,必须为字节 
        byte[] sendData;
        //接收的数据长度 
        int recvLen = 0;
        //连接线程
        Thread connectThread;
    
        bool isClientActive = false;
    
        //连接服务器时发送的vector3类型
        Vector3 startVec = Vector3.zero;
    
        bool isStartHeart = false;
    
    
        int port;
    
        //判断是否让客户端重新发送数据包
        bool isReSend = false;
        string reSendStrIndex;
    
        public delegate void ReSendIndexDeledate(string str);
        public event ReSendIndexDeledate ReSendIndexEvent;
    
    
        private void Awake()
        {
            instance = this;
        }
    
        void Start()
        {
            UDPClientIP = "127.0.0.1";//服务端的IP.自己更改
            port = 9000;
            UDPClientIP = UDPClientIP.Trim();
            isClientActive = true;
            InitSocket(); //在这里初始化
        }
    
        private void Update()
        {
            if (isStartHeart)
            {
                HeartSend();
            }
            //检测心跳与心跳反馈的间隔时间,
            timerInterval += Time.deltaTime;
    
            if (timerInterval > 6f)
            {
                print("连接异常");
                timerInterval = 0f;
            }
    
    
            if (isReSend)
            {
                print(111);
                if (ReSendIndexEvent!=null)
                {
                    ReSendIndexEvent(reSendStrIndex);
                    reSendStrIndex = null;
                    isReSend = false;
                }
            }
        }
    
        //初始化 
        void InitSocket()
        {
            //定义连接的服务器ip和端口,可以是本机ip,局域网,互联网 
            ipEnd = new IPEndPoint(IPAddress.Parse(UDPClientIP), port);
            //定义套接字类型,在主线程中定义 
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //socket.Bind(ipEnd);
            //定义服务端 
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            serverEnd = (EndPoint)sender;
            print("local:等待连接");
            isStartHeart = true;
            //开始心跳监听
            //客户端发送心跳消息后,计时器开始计时,判断3秒内是否能收到服务端的反馈
            HeartSend();
            //开启一个线程连接,否则主线程卡死 
            connectThread = new Thread(new ThreadStart(SocketReceive));
            connectThread.Start();
        }
    
        //发送字符串
        public void SocketSend(string sendStr)
        {
            //清空发送缓存 
            sendData = new byte[1500];
            //数据类型转换 
            sendData = Encoding.UTF8.GetBytes(sendStr);
            //发送给指定服务端
            socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd);
        }
    
        //发送消息频率
        float timerRate = 5;
        //接收服务端心跳反馈的时间间隔
        float timerInterval = 0f;
    
        byte[] heartSendData = new byte[1024];
    
        /// <summary>
        /// 心跳
        /// </summary>
        void HeartSend()
        {
            timerRate += Time.deltaTime;
            if (timerRate > 5f)
            {
                try
                {
                    SocketSend("alive");
                }
                catch 
                {
                }
                timerRate = 0f;
            }
        }
    
        //客户端接收服务器消息
        void SocketReceive()
        {
            //进入接收循环 
            while (isClientActive)
            {
                //对data清零 
                recvData = new byte[20000];
                try
                {
                    //获取服务端端数据
                    recvLen = socket.ReceiveFrom(recvData, ref serverEnd);
                    if (isClientActive == false)
                    {
                        break;
                    }
                }
                catch
                {
    
                }
                //输出接收到的数据 
                if (recvLen > 0)
                {
                    //接收到的信息
                    recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
                }
                print("server:" + recvStr);
    
                //心跳回馈
                if (recvStr == "keeping")
                {
                    // 当服务端收到客户端发送的alive消息时
                    print("连接正常");
                    timerInterval = 0;
                }
                else if(recvStr!=null)
                {
                    reSendStrIndex = recvStr;
                    isReSend = true;
                }
            }
        }
    
        //连接关闭
        void SocketQuit()
        {
            //最后关闭socket
            if (socket != null)
                socket.Close();
        }
        void OnApplicationQuit()
        {
            isStartHeart = false;
            isClientActive = false;
            SocketQuit();
            Thread.Sleep(25);
        }
    }
    

    SendImageScript:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System;
    using System.IO;
    using System.Threading;
    public class SendImageScript : MonoBehaviour {
        
        byte[] imagebytes;
        
        string newImageString;
        string path;
    
        private void Start()
        {
            NewUDPClient.instance.ReSendIndexEvent += GetReSendIndexFromUDPClient;
            path = Application.streamingAssetsPath + "/" + "222.jpg";
    
            FileStream files = new FileStream(path, FileMode.Open);
            imagebytes = new byte[files.Length]; files.Read(imagebytes, 0, imagebytes.Length); files.Close();
            files.Close();
            picStr = Convert.ToBase64String(imagebytes);
        }
    
    
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
              
                StartCoroutine(SendPicture());
            }
        }
    
        string picStr;
        IEnumerator SendPicture() {
            UDPSplit(picStr);
            //将图片发送给投影机
           yield return new WaitForSeconds(0.1f);
    
            for (int i = 0; i < num - 1000; i++)
            {
                if (UDPStringDic.TryGetValue(i, out newImageString))
                {
                    NewUDPClient.instance.SocketSend(newImageString);
                }
            }
    
            yield return new WaitForSeconds(0.1f);
            //发送完成后发一条信息给服务端
            NewUDPClient.instance.SocketSend("done");
        }
    
        string[] reSendNum;
        int newindex;
        void GetReSendIndexFromUDPClient(string str)
        {
            reSendNum = str.Split('_');
    
            for (int i = 0; i < reSendNum.Length; i++)
            {
                if (int.TryParse(reSendNum[i], out newindex))
                {
                    if (UDPStringDic.TryGetValue(newindex, out newImageString))
                    {
                        NewUDPClient.instance.SocketSend(newImageString);
                    }
                }
            }
            //发送完成后发一条信息给服务端
            NewUDPClient.instance.SocketSend("done");
            print("重新发送完毕");
    
        }
    
    
    
        Dictionary<int, string> UDPStringDic = new Dictionary<int, string>();
        int index = 0;
        int maxIndex = 1000;
        string newstr;
        int num;
        
        void UDPSplit(string str)
        {
            index = 0;
            maxIndex = 1000;
            int stringTag = 1000;
            UDPStringDic.Clear();
            num = (str.Length / 1000) + 1 + 1000;   //将数字变成四位数的,三个字节
                                                    //  print(num-1000);
            for (int i = 0; i < num - 1000; i++)
            {
                if (maxIndex > str.Length - index)
                {
                    maxIndex = str.Length - index;
                }
                newstr = "1551683020" + "_" + num + "_" + stringTag + "_" + str.Substring(index, maxIndex); //包名,包长,包的顺序号,包的内容
    
    
                UDPStringDic.Add(stringTag - 1000, newstr);
                stringTag++;
                index += 1000;
            }
        }
    
    
        public static long GetTimeStamp(bool bflag = true)
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            long ret;
            if (bflag)
                ret = Convert.ToInt64(ts.TotalSeconds);
            else
                ret = Convert.ToInt64(ts.TotalMilliseconds);
            return ret;
        }
    
        private void OnDisable()
        {
            NewUDPClient.instance.ReSendIndexEvent -= GetReSendIndexFromUDPClient;
        }
    }
    

    服务端代码

    NewUDPServer:

    using UnityEngine;
    using System.Collections.Generic;
    using System.Collections;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System;
    using System.IO;
    using UnityEngine.UI;
    
    public class NewUDPServer : MonoBehaviour {
    
        public static NewUDPServer instance;
        Socket socket; //目标socket
        EndPoint clientEnd; //客户端
        IPEndPoint ipEnd; //侦听端口
        [HideInInspector]
        public string recvStr; //接收的字符串
        string sendStr; //发送的字符串
        byte[] recvData; //接收的数据,必须为字节
        byte[] sendData = new byte[1024]; //发送的数据,必须为字节
        int recvLen; //接收的数据长度
        Thread connectThread; //连接线程
        [HideInInspector]
        public bool isStartSend = false;
        int port;
    
        bool isSendImage;
        public delegate void UDPServerDeledate(Texture2D byths);
        public event UDPServerDeledate UDPserverEvent;
    
        //接收到的图片字节数组的图片字节长度
        int imageLength;
    
        string imageStr;
    
        /// <summary>
        /// 初始化
        /// </summary>
        void InitSocket()
        {
            //定义侦听端口,侦听任何IP 
            ipEnd = new IPEndPoint(IPAddress.Any, port);
            //定义套接字类型,在主线程中定义 
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            //服务端需要绑定ip 
            socket.Bind(ipEnd);
            //定义客户端
            IPEndPoint sender = new IPEndPoint(IPAddress.Broadcast, 0);
            clientEnd = (EndPoint)sender;
            print("local:等待连接数据");
            //开启一个线程连接
            connectThread = new Thread(new ThreadStart(SocketReceive));
            connectThread.Start();
        }
    
        /// <summary>
        /// 服务器向客户端发送消息
        /// </summary>
        /// <param name="sendStr"></param>
        public void SocketSend(string sendStr)
        {
            //清空发送缓存 
            sendData = new byte[20000];
            //数据类型转换 
            sendData = Encoding.UTF8.GetBytes(sendStr);
            //发送给指定客户端 
            socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
        }
    
        bool isServerActive = false;
    
        /// <summary>
        /// 服务器接收来自客户端的消息
        /// </summary>
        void SocketReceive()
        {
            //进入接收循环 
            while (isServerActive)
            {
                //对data清零 
                recvData = new byte[1500];
                try
                {
                    //获取服务端端数据
                    recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
                    if (isServerActive == false)
                    {
                        break;
                    }
                }
                catch
                {
    
                }
    
                if (recvLen > 0)
                {
                    recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
                    //输出接收到的数据 
                    // Debug.Log("Clent:__" + recvStr + "++" + recvLen);
                    //客户端心跳回馈
                    if (recvStr == "alive")
                    {
                        HeartCheck();
                    }
                    else if (recvStr == "done")
                    {
                        //当接收到的信息为done时,判断接收到的图片包数量是否够,不够就发送未收到的包的标识号,让客户端再发送一下
                        CheckPackage();
    
                    }
                    else if (recvLen > 18) //图片包头为29个字节
                    {
                        // print("这是图片");
                        //合并发来的图片
                        ConmbineString(recvStr);
                    }
                }
            }
        }
    
        //未发送包的标识号
        string reSendPackageindex;
        /// <summary>
        /// 当接收到客户端发送的done消息后,判断接收到的图片包是否完整
        /// </summary>
        void CheckPackage()
        {
            reSendPackageindex = null;
            if (doneIndex.Count <= 0)
            {
                print("接收成功");
                for (int i = 0; i < newImageDic.Count; i++)
                {
                    if (newImageDic.TryGetValue(i, out dicStr))
                    {
                        newConbineStr = newConbineStr + dicStr;
                    }
                }
                isSendImage = true;
                newImageCount = 0;
                newStrIndex = 0;
                isFirst = true;
                newImageDic.Clear();
                doneIndex.Clear();
            }
            else
            {
                print("接收失败,重新请求");
                //判断哪些包没有收到
                for (int i = 0; i < doneIndex.Count; i++)
                {
                    reSendPackageindex = doneIndex[i] + "_" + reSendPackageindex;
                }
    
                SocketSend(reSendPackageindex);
                print("请求发送未成功包");
            }
        }
    
        
        string newConbineStr;
        string newImageName;
        int newImageCount = 0;
        int newStrIndex = 0;
        string newImageMessage;
        //判断是否是第一次接受消息
        bool isFirst = true;
        string oldImageName;
        Dictionary<int, string> newImageDic = new Dictionary<int, string>();
        List<int> doneIndex = new List<int>();
    
        string dicStr;
        //将分包发来的消息合成一个包
        void ConmbineString(string perStr)
        {
            //0.图片名字(21字节)--1.包的长度(1000为起始点,4字节)--2.包的下标(1000为起始点4个字节)--3.包的内容
            //分割字符串 "_"
            string[] strs = perStr.Split('_');
            //名字
            newImageName = strs[0];
            newImageCount = int.Parse(strs[1]) - 1000;
            newStrIndex = int.Parse(strs[2]) - 1000;
            newImageMessage = strs[3];
    
            if (isFirst)
            {
                oldImageName = newImageName;
                isFirst = false;
                newConbineStr = null;
                //将将要收到的包的标识号存进集合里边,每接收到对应的数据就移除该标识号
                for (int i = 0; i < newImageCount; i++)
                {
                    doneIndex.Add(i);
                }
            }
            
            if (newImageName == oldImageName)
            {
                // print(newImageCount);
                if (!newImageDic.ContainsKey(newStrIndex))
                {
                    //每接收到对应的数据就移除该标识号
                    try
                    {
                        doneIndex.Remove(newStrIndex);
                    }
                    catch
                    {
                        print("数据传输失败");
                    }
    
                    newImageDic.Add(newStrIndex, newImageMessage);
                }
            }
        }
    
        float timerInterval = 0;
        bool isStartCheck = false;
    
        void HeartCheck()
        {
            isStartCheck = true;
            timerInterval = 0f;
            SocketSend("keeping");
            print("连接正常");
        }
    
        void Update()
        {
            timerInterval += Time.deltaTime;
            if (isStartCheck)
            {
                if (timerInterval > 6f)
                {
                    print("网络连接异常");
                    timerInterval = 0f;
                }
            }
    
            if (isSendImage)
            {
                ParseBYTeArr(newConbineStr);
                newConbineStr = null;
                isSendImage = false;
            }
        }
    
        /// <summary>
        /// 发来的字节包括:图片的字节长度(前四个字节)和图片字节
        /// 得到发来的字节中图片字节长度和图片字节数组
        /// </summary>
        void ParseBYTeArr(string receStr)
        {
            byte[] bytes = Convert.FromBase64String(receStr);
    
            string timestamp = GetTimeStamp().ToString();
            string filename = Application.streamingAssetsPath + "/Imags/"+timestamp + ".jpg";
            File.WriteAllBytes(filename, bytes);
    
            Texture2D tex2D = new Texture2D(100, 100);
            tex2D.LoadImage(bytes);
    
            if (UDPserverEvent != null)
            {
                UDPserverEvent(tex2D);
            }
        }
    
    
        //连接关闭
        void SocketQuit()
        {
            //最后关闭socket
            if (socket != null)
            {
                try
                {
                    socket.Close();
                }
                catch
                {
    
                }
            }
    
            Debug.LogWarning("local:断开连接");
        }
    
        private void Awake()
        {
            instance = this;
        }
    
        void Start()
        {
            port = 9000;
            isServerActive = true;
            InitSocket(); //在这里初始化server
        }
    
        void OnDisable()
        {
            isServerActive = false;
            SocketQuit();
            Thread.Sleep(100);
        }
    
        public static long GetTimeStamp(bool bflag = true)
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            long ret;
            if (bflag)
                ret = Convert.ToInt64(ts.TotalSeconds);
            else
                ret = Convert.ToInt64(ts.TotalMilliseconds);
            return ret;
        }
    }
    

    LoadImageFromClient:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    public class LoadImageFromClient : MonoBehaviour {
    
        public RawImage newImage;
        public Transform showPanel;
    
        void Start()
        {
            NewUDPServer.instance.UDPserverEvent += ReceiveByteFromUDPServer;
        }
    
        /// <summary>
        /// 发来的字节包括:图片的字节长度(前四个字节)和图片字节
        /// 得到发来的字节中图片字节长度和图片字节数组
        /// </summary>
        void ReceiveByteFromUDPServer(Texture2D newTexture)
        {
            newImage.gameObject.SetActive(true);
            newImage.texture = newTexture;
            Invoke("SetDefultImage", 5f);
        }
    
        void SetDefultImage() {
            newImage.texture = null;
        }
    
        void OnDisable()
        {
            NewUDPServer.instance.UDPserverEvent -= ReceiveByteFromUDPServer;
        }
    }
    

    相关文章

      网友评论

          本文标题:untiy 使用UDP发送图片,并将接收到的图片保存到本地

          本文链接:https://www.haomeiwen.com/subject/skqnuqtx.html