IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> unity使用Socket进行文件上传 -> 正文阅读

[游戏开发]unity使用Socket进行文件上传

一、制作思路

1.首先大家要先了解一下、socket一些接口的使用

https://blog.csdn.net/qq_42345116/article/details/122185529

2.思路

2.1首先 我们知道Socket之间传输的数据是Byte数组,所以我们客户端要上传的文件必须是byte数组文件

使用这个方法将文件转换为byte[]数组

#region 将文件转换成byte[] 数组

    /// <summary>
    /// 将文件转换成byte[] 数组
    /// </summary>
    /// <param name="fileUrl">文件路径文件名称</param>
    /// <returns>byte[]</returns>
    protected byte[] GetFileData(string fileUrl)
    {
        FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read);
        lock (fs)
        {
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                if (fs != null)
                {
                    //关闭资源
                    fs.Close();
                }
            }
        }
    }
    #endregion

2.2相应的服务器接收到客户端的byte数组文件后要将其反转成文件进行保存

    #region 将byte数组转换为文件并保存到指定地址
    /// <summary>
    /// 将byte数组转换为文件并保存到指定地址
    /// </summary>
    /// <param name="buff">byte数组</param>
    /// <param name="savepath">保存地址</param>
    public static void Bytes2File(byte[] buff, string savepath)
    {
        if (File.Exists(savepath))
        {
            File.Delete(savepath);
        }

        FileStream fs = new FileStream(savepath, FileMode.CreateNew);
        BinaryWriter bw = new BinaryWriter(fs);
        bw.Write(buff, 0, buff.Length);
        bw.Close();
        fs.Close();
    }
    #endregion

2.3但是保存文件需要地址(F:/)+文件名称(XXX文件名)+文件类型?(.png)但是我们客户端传过来的byte数组没有这些信息,所以这里我用了Json数据进行组合传输

//1.Json数据类
[Serializable]
public class JsonData
{
    public string flieName;      //文件名称(文件名称)
    public byte[] fileData;      //文件byte[]数据
}

//2.获取文件名称 类型
 string[] splitData=path.Slip('/'); //将文件路径拆分 为了获取文件名和文件类型
 jsonData = new JsonData()
 {
    flieName = splitData[splitData.Length - 1], //拆分的最后一个一定是文件名.文件类型
    fileData = GetFileData(filePath) //将文件转换为byte[]
 };

string data=JsonMapper.ToJson(jsonData);//转换成json
byte[] sendData = Encoding.UTF8.GetBytes(data)//将string转换成byte数组

这样我们就获取到一个json的byte数组数据。

2.4但是还没完,要知道在网络中数据是用包来传输的,每个包能存放的byte数只有那么多,超出了就要分包发送,文件总是很大的,肯定要分包发送,但是我们服务器没有办法判断是否已经接收完客户端的数据,所以我们客户端需要在服务器接收数据前先发送一个byte数组(这个数组信息代表了文件的总大小),服务器知道大小以后就开始整合数据,直到接收数据大小等于发送的数据大小后,开始转换byte数据

//1.接上文sendData就是我们要发送的json byte数组,我们获取他的长度通过Send发送给服务器
clientSocket.Send(Encoding.UTF8.GetBytes(sendData.Length.ToString()));

服务器接收处理

//2.这里是服务器要处理

  private byte[] result = new byte[1024]; //1.存入的byte值 最大数量1024
    private byte[] subpackageData; //1.分包数据存入信息

    int dataLength;     //要接收信息总的长度
    bool isEndData = false; //是否接收到信息总长度
    //开启线程接收数据 (将Socket作为值传入)
    private void ReceiveMessage(object clientSocket)
    {
        Socket myClientSocket = (Socket)clientSocket; //2.转换传入的客户端Socket
        while (true)
        {
            try
            {
                //接收数据  
                int receiveNumber = myClientSocket.Receive(result); //3.将客户端得到的byte值写入

                //没有接收信息总长度
                if (!isEndData)
                {
                    //接收
                    dataLength = int.Parse(Encoding.UTF8.GetString(result));
                    subpackageData = new byte[0]; //初始化缓冲区
                    Debug.Log("1.接收到信息的总长度: " + dataLength);
                    isEndData = true;
                }
                else
                {
                    //有数据 并且 接收到信息长度后执行
                    if (receiveNumber > 0 && isEndData)
                    {
                        //1.整合数据
                        byte[] newResult = new byte[(subpackageData.Length + receiveNumber)];
                        Array.Copy(subpackageData, 0, newResult, 0, subpackageData.Length); //将上一次分包数据赋值给容器
                        Array.Copy(result, 0, newResult, subpackageData.Length, receiveNumber); //将当前收到的数据赋值给容器

                        subpackageData = newResult;
                        Debug.Log("整合后数据长度: " + subpackageData.Length);


                        //整合的数据超出 或等于数据
                        if (subpackageData.Length >= dataLength)
                        {
                            isEndData = false; //设置没有接收到信息总长度
                            //数据解析
                            string data = Encoding.UTF8.GetString(subpackageData);
                            Debug.Log(data);
                            DisposeData(data);

                            myClientSocket.Send(Encoding.UTF8.GetBytes("文件转换成功" + DateTime.Now));
                        }
                    }
                    else
                    {
                        Debug.Log("client: " + ((IPEndPoint)myClientSocket.RemoteEndPoint).Address.ToString() + "断开连接");
                        threadDic[((IPEndPoint)myClientSocket.RemoteEndPoint).Address.ToString()].Abort(); //清除线程
                    }
                }

            }
            catch (Exception ex)
            {
                //myClientSocket.Shutdown(SocketShutdown.Both); //出现错误 关闭Socket
                Debug.Log(" 错误信息" + ex); //打印错误信息
                break;
            }
        }
    }

二、服务器最终代码

using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

//Json数据
[Serializable]
public class JsonData
{
    public string operationType; //服务器事件类型(选择服务器要执行的方法)
    public string clientIP;      //客户端本地ip
    public string flieName;      //文件名称(文件名称)
    public byte[] fileData;      //文件byte[]数据
}

public class SocketServer : MonoBehaviour
{
    private string myip;
    private int myPort;   //端口  
    string saveFilePath;  //保存地址
    JsonData jsonData;

    static Socket serverSocket;
    Thread myThread;
    Dictionary<string, Thread> threadDic = new Dictionary<string, Thread>();//存储线程,程序结束后关闭线程
    private void Start()
    {
        myip = ConfigFile.LoadString("Server");
        myPort = int.Parse(ConfigFile.LoadString("port"));
        saveFilePath = Application.streamingAssetsPath + "/"; //文件保存路径


        //服务器IP地址  ,127.0.0.1 为本机IP地址
        IPAddress ip = IPAddress.Parse(myip);
        //IPAddress ip = IPAddress.Any; //本机地址
        Debug.Log(ip.ToString());
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        IPEndPoint iPEndPoint = new IPEndPoint(ip, myPort);
        serverSocket.Bind(iPEndPoint);  //绑定IP地址:端口  
        serverSocket.Listen(10);        //最多10个连接请求  
                                        //Console.WriteLine("creat service {0} success",
                                        //    serverSocket.LocalEndPoint.ToString());

        myThread = new Thread(ListenClientConnect);
        myThread.Start();
        Debug.Log("服务器启动...........");

    }

    // 监听客户端是否连接  
    private void ListenClientConnect()
    {
        while (true)
        {
            Socket clientSocket = serverSocket.Accept(); //1.创建一个Socket 接收客户端发来的连接请求 没有时堵塞
            string clientIp = ((IPEndPoint)clientSocket.RemoteEndPoint).Address.ToString();

            Debug.Log("客户端连接成功:" + clientIp);
            clientSocket.Send(Encoding.UTF8.GetBytes("连接成功")); //2.向客户端发送 连接成功 消息
            Thread receiveThread = new Thread(ReceiveMessage); //3.为已经连接的客户端创建一个线程 此线程用来处理客户端发送的消息
            receiveThread.Start(clientSocket); //4.开启线程

            //将已经连接的客户端添加到字典中
            if (!threadDic.ContainsKey(clientIp))
            {
                threadDic.Add(clientIp, receiveThread);
            }
        }
    }


    private byte[] result = new byte[1024]; //1.存入的byte值 最大数量1024
    private byte[] subpackageData; //1.分包数据存入信息

    int dataLength;     //要接收信息总的长度
    bool isEndData = false; //是否接收到信息总长度
    //开启线程接收数据 (将Socket作为值传入)
    private void ReceiveMessage(object clientSocket)
    {
        Socket myClientSocket = (Socket)clientSocket; //2.转换传入的客户端Socket
        while (true)
        {
            try
            {
                //接收数据  
                int receiveNumber = myClientSocket.Receive(result); //3.将客户端得到的byte值写入

                //没有接收信息总长度
                if (!isEndData)
                {
                    //接收
                    dataLength = int.Parse(Encoding.UTF8.GetString(result));
                    subpackageData = new byte[0]; //初始化缓冲区
                    Debug.Log("1.接收到信息的总长度: " + dataLength);
                    isEndData = true;
                }
                else
                {
                    //有数据 并且 接收到信息长度后执行
                    if (receiveNumber > 0 && isEndData)
                    {
                        //1.整合数据
                        byte[] newResult = new byte[(subpackageData.Length + receiveNumber)];
                        Array.Copy(subpackageData, 0, newResult, 0, subpackageData.Length); //将上一次分包数据赋值给容器
                        Array.Copy(result, 0, newResult, subpackageData.Length, receiveNumber); //将当前收到的数据赋值给容器

                        subpackageData = newResult;
                        Debug.Log("整合后数据长度: " + subpackageData.Length);


                        //整合的数据超出 或等于数据
                        if (subpackageData.Length >= dataLength)
                        {
                            isEndData = false; //设置没有接收到信息总长度
                            //数据解析
                            string data = Encoding.UTF8.GetString(subpackageData);
                            Debug.Log(data);
                            DisposeData(data);

                            myClientSocket.Send(Encoding.UTF8.GetBytes("文件转换成功" + DateTime.Now));
                        }
                    }
                    else
                    {
                        Debug.Log("client: " + ((IPEndPoint)myClientSocket.RemoteEndPoint).Address.ToString() + "断开连接");
                        threadDic[((IPEndPoint)myClientSocket.RemoteEndPoint).Address.ToString()].Abort(); //清除线程
                    }
                }

            }
            catch (Exception ex)
            {
                //myClientSocket.Shutdown(SocketShutdown.Both); //出现错误 关闭Socket
                Debug.Log(" 错误信息" + ex); //打印错误信息
                break;
            }
        }
    }

    #region 数据处理
    private void DisposeData(string data)
    {
        //UTF8Encoding m_utf8 = new UTF8Encoding(false); //这是不带有BOM的UTF-8 使用带有BOM的UTF-8转换Json容易失败
        jsonData = JsonMapper.ToObject<JsonData>(data);

        string savePath = saveFilePath + jsonData.flieName;
        Debug.Log(savePath);
        //上传附件 判断是否存在
        if (File.Exists(savePath))
        {
            //文件存在 重命名新文件
            savePath = saveFilePath + "(Colon)"+jsonData.flieName;
        }

        Bytes2File(jsonData.fileData, savePath); //将byte数据转换为文件
    }
    #endregion

    #region 将byte数组转换为文件并保存到指定地址
    /// <summary>
    /// 将byte数组转换为文件并保存到指定地址
    /// </summary>
    /// <param name="buff">byte数组</param>
    /// <param name="savepath">保存地址</param>
    public static void Bytes2File(byte[] buff, string savepath)
    {
        if (File.Exists(savepath))
        {
            File.Delete(savepath);
        }

        FileStream fs = new FileStream(savepath, FileMode.CreateNew);
        BinaryWriter bw = new BinaryWriter(fs);
        bw.Write(buff, 0, buff.Length);
        bw.Close();
        fs.Close();
    }
    #endregion

    void OnApplicationQuit()
    {
        //结束线程必须关闭 否则下次开启会出现错误 (如果出现的话 只能重启unity了)
        myThread.Abort();

        //关闭开启的线程
        foreach (string item in threadDic.Keys)
        {
            Debug.Log(item);//de.Key对应于key/value键值对key
            //item.Value.GetType()
            threadDic[item].Abort();
        }
    }

}

三、客户端最终代码

using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine.UI;
using UnityEngine;
using System;
using System.IO;
using LitJson;

//Json数据
[Serializable]
public class JsonData
{
    public string operationType = string.Empty; //服务器事件类型(选择服务器要执行的方法)
    public string clientIP = string.Empty;      //客户端本地ip
    public string flieName = string.Empty;      //文件名称(文件名称)
    public byte[] fileData = new byte[] { };    //文件byte[]数据
}

public class SocketClient : MonoBehaviour
{
    private string myip;
    private int myPort;   //端口  
    JsonData jsonData;    //整合的json数据

    public Button sendBtn;//发送
    public InputField path;//信息
    Socket clientSocket;

    byte[] sendData; //要发送的数据
    private static byte[] result = new byte[1024]; //接收服务器的数据

    string filePath;
    void Start()
    {
        myip = ConfigFile.LoadString("Server");
        myPort = int.Parse(ConfigFile.LoadString("port"));

        //按钮监听
        sendBtn.onClick.AddListener(delegate {

            //判断 路径不为空 并且 文件存在
            if (!path.text.Equals(string.Empty)&& File.Exists(path.text)) 
            {
               
                //判断 文件是否存在\反斜杠
                filePath = path.text.Contains("\\") ? path.text.Replace('\\', '/') : path.text;
                Debug.Log(filePath);
                try
                {
                    //获取文件名称 类型
                    FieldSplit(filePath, '/');
                    jsonData = new JsonData()
                    {
                        operationType = "上传附件",
                        clientIP = GetLocalIP.GetIP(GetLocalIP.ADDRESSFAM.IPv4),
                        flieName = splitData[splitData.Length - 1],
                        fileData = GetFileData(filePath) //将文件转换为byte[]
                    };

                    Debug.Log(jsonData.flieName);

                    sendData = Encoding.UTF8.GetBytes(JsonMapper.ToJson(jsonData)); //将json数据转换为byte

                    //1.先发送 数据长度
                    clientSocket.Send(Encoding.UTF8.GetBytes(sendData.Length.ToString()));
                    Debug.Log("byte数量:" + sendData.Length.ToString());
                    //2.发送内容
                    clientSocket.Send(sendData);//传送信息
                }
                catch (Exception ex)
                {
                    Debug.Log("发送失败:" + ex);
                }

            }

        });

        //监听连接的客户端连接
        ListeningClient();

    }

    #region 获取文件名称 类型
    string[] splitData; 
    private void FieldSplit(string field,char _value)
    {
        splitData = field.Split(_value);
    }

    #endregion

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }
    }

    #region 监听连接的客户端连接
    private void ListeningClient() 
    {
        //要连接的服务器IP地址  
        IPAddress ip = IPAddress.Parse(myip);//本地IP地址
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            clientSocket.Connect(new IPEndPoint(ip, myPort)); //配置服务器IP与端口 ,并且尝试连接
        }
        catch (Exception ex)
        {
            Debug.Log("Connect lose" + ex); //打印连接失败
            return;
        }

        int receiveLength = clientSocket.Receive(result);//接收服务器回复消息,成功则说明已经接通
        Debug.Log("服务器消息长度:" + receiveLength);

        if (receiveLength > 1)
        {
            Debug.Log("接收服务器消息:" + Encoding.UTF8.GetString(result));
        }
    }

    #endregion


    #region 将文件转换成byte[] 数组

    /// <summary>
    /// 将文件转换成byte[] 数组
    /// </summary>
    /// <param name="fileUrl">文件路径文件名称</param>
    /// <returns>byte[]</returns>
    protected byte[] GetFileData(string fileUrl)
    {
        FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read);
        lock (fs)
        {
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                if (fs != null)
                {
                    //关闭资源
                    fs.Close();
                }
            }
        }
    }
    #endregion
}

四、其他脚本:

1.获取本地ip

using System.Collections;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using UnityEngine;

public class GetLocalIP 
{
    #region 获取本地IP地址
    public enum ADDRESSFAM
    {
        IPv4, IPv6
    }

    /// <summary>
    /// 获取本机IP
    /// </summary>
    /// <param name="Addfam">要获取的IP类型</param>
    /// <returns></returns>
    public static string GetIP(ADDRESSFAM Addfam)
    {
        if (Addfam == ADDRESSFAM.IPv6 && !Socket.OSSupportsIPv6)
        {
            return null;
        }

        string output = "";

        foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
        {
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
            NetworkInterfaceType _type1 = NetworkInterfaceType.Wireless80211;
            NetworkInterfaceType _type2 = NetworkInterfaceType.Ethernet;

            if ((item.NetworkInterfaceType == _type1 || item.NetworkInterfaceType == _type2) && item.OperationalStatus == OperationalStatus.Up)
#endif 
            {
                foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
                {
                    //IPv4
                    if (Addfam == ADDRESSFAM.IPv4)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            output = ip.Address.ToString();
                        }
                    }

                    //IPv6
                    else if (Addfam == ADDRESSFAM.IPv6)
                    {
                        if (ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
                        {
                            output = ip.Address.ToString();
                        }
                    }
                }
            }
        }
        return output;
    }

    #endregion
}

2.获取xml

using System.Xml.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class ConfigFile
{
    static string path = Application.dataPath + "/StreamingAssets/ConfigFile.xml";

    public static string LoadString(string str)
    {
        XDocument document = XDocument.Load(path);
        //获取到XML的根元素进行操作
        XElement root = document.Root;
        XElement ele = root.Element(str);
        return ele.Value;
    }
}

3.ConfigFile.xml 配置文件 放到StreamingAssets文件夹下

<?xml version="1.0" encoding="utf-8"?>
<Info>

  <!--服务器ip配置-->
  <Server>127.0.0.1</Server>
  <port>9999</port>
</Info>

4.json插件

链接:https://pan.baidu.com/s/14SgSgrK14zdGURfXVjeyjg?
提取码:syq1

最终效果:

客户端:

?服务器:

?上传成功? 我们看一下内容是否有问题

客户端发送的文件:

?服务器接收的文件:

内容没问题 成功。。。?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-01-01 14:17:32  更:2022-01-01 14:17:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 2:54:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码