Unity实现Udp服务端的基本功能

在之前的博客中我写了关于简单实现Udp通信客户端的Demo,在实际项目中,我们有时候也要写Udp服务端的功能,以便于接收客户端发送过来的信号。在这篇博客中我简要介绍下Udp服务端的实现方法。

步骤

1.为了能够方便实现动态修改Udp服务端Ip和端口号功能,在这里首先加一个读取配置文件的功能,首先在工程中新建一个StreamingAssets文件夹,在里面新建一个Config.txt文件,切记要将其保存为UTF-8文件,如下图所示:

unity udp发送16禁止 unity接收udp_unity


2.在Config.txt中输入如下内容,确定UdpServer和IP和端口号,以及客户端的端口号,如下图所示:

unity udp发送16禁止 unity接收udp_网络协议_02


3.完成ComfigTest.cs脚本,将该脚本挂载到场景中的物体上,脚本代码如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

public class ConfigTest : MonoBehaviour
{
    private string configPath;

    public Dictionary<string, Dictionary<string, string>> dic;

	// Use this for initialization
	void Awake ()
    {
        //读取配置文件(StreamingAssets)路径
        configPath = Path.Combine(Application.streamingAssetsPath,"Config.txt");
        if (dic == null)
        {
            dic = new Dictionary<string, Dictionary<string, string>>();
            LoadConfig();
        }
	}

    /// <summary>
    /// 处理所有的数据
    /// </summary>
    private void LoadConfig()
    {
        string[] lines = null;
        if (File.Exists(configPath))
        {
            lines = File.ReadAllLines(configPath);
            BuildDic(lines);
        }
    }

    /// <summary>
    /// 处理所有的数据
    /// </summary>
    /// <param name="lines"></param>
    void BuildDic(string[] lines)
    {
        string mainKey = null;//主键
        string subKey = null;//子键
        string subValue = null;//值
        foreach (var item in lines)
        {
            string line = null;
            line = item.Trim();//去除空白行
            if (!string.IsNullOrEmpty(line))
            {
                if (line.StartsWith("["))//取主键
                {
                    mainKey = line.Substring(1, line.IndexOf("]") - 1);
                    dic.Add(mainKey, new Dictionary<string, string>());
                }
                else//取子键
                {
                    var configValue = line.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    subKey = configValue[0].Trim();
                    subValue = configValue[1].Trim();
                    subValue = subValue.StartsWith("\"") ? subValue.Substring(1) : subValue;
                    dic[mainKey].Add(subKey,subValue);
                }
            }
        }
    }
}

4.接着实现UdpServer功能,首先创建UdpServer.cs脚本,脚本代码如下所示,完成后将此脚本挂载到场景中的物体上:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using System.Runtime.InteropServices;

namespace ImmerUDP
{
    public class UdpServer : MonoBehaviour
    {
        [Header("本地端口"), SerializeField]
        private int localPort;

        [Header("目标端口"), SerializeField]
        private int endPort;

        [Header("打开接收功能")]
        public bool openReceive = true;

        [Header("打开发送功能")]
        public bool openSend = true;
        //服务器Socket对象
        private Socket serverSocket;
        //udp客户端socket
        private UdpClient client;

        private EndPoint epSender;

        private IPEndPoint endPoint;
        //接收数据的字符串
        private byte[] ReceiveData = new byte[1024];
        //配置文件管理类
        public ConfigTest configTest;
        //点击进入游戏的字符串
        private string ToGameString;
        
        //显示接收的信号
        public string receiveString = "";
        //对应IP地址
        private string serverIP;
        //是否可以接收字符串
        public bool isCanReceive = true;
        //接收到的数据显示Text
        public Text reciveText;
        
        // Use this for initialization
        void Start()
        {
            BeginString();
            StartCoroutine(ToBeginSocket0());
        }

        /// <summary>
        /// 初始化配置文件
        /// </summary>
        void BeginString()
        {
            localPort = int.Parse(configTest.dic["端口号"]["本地"]);
            endPort = int.Parse(configTest.dic["端口号"]["对应"]);
            serverIP = configTest.dic["IP"]["ip0"];
            ToGameString = configTest.dic["UDP"]["进入游戏"];
        }

        /// <summary>
        /// 初始化Socket的协程
        /// </summary>
        /// <returns></returns>
        IEnumerator ToBeginSocket0()
        {
            yield return new WaitForSeconds(0.1f);
            ToBeginSocket();
        }

        /// <summary>
        /// 初始化Socket
        /// </summary>
        void ToBeginSocket()
        {
            if (openReceive)
            {
                //服务器Socket对实例化
                serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
                //Socket对象服务器的IP和端口固定
                serverSocket.Bind(new IPEndPoint(IPAddress.Any,localPort));
                //监听的端口和地址
                epSender = (EndPoint)new IPEndPoint(IPAddress.Any,0);
                //开始异步接收数据
                serverSocket.BeginReceiveFrom(ReceiveData,0,ReceiveData.Length,SocketFlags.None,ref epSender,new AsyncCallback(ReceiveFromClients),epSender);
            }
            if (openSend)
            {
                client = new UdpClient();
                //目标端口和地址
                endPoint = new IPEndPoint(IPAddress.Parse(serverIP),endPort);
            }
        }
        // Update is called once per frame
        void Update()
        {
            UdpControl();
        }

        /// <summary>
        /// 异步加载,处理数据
        /// </summary>
        /// <param name="iar"></param>
        void ReceiveFromClients(IAsyncResult iar)
        {
            int reve = serverSocket.EndReceiveFrom(iar,ref epSender);
            //数据处理
            string str = System.Text.Encoding.UTF8.GetString(ReceiveData,0,reve);
            //把得到的数据传给数据处理中心
            serverSocket.BeginReceiveFrom(ReceiveData,0,ReceiveData.Length,SocketFlags.None,ref epSender,new AsyncCallback(ReceiveFromClients),epSender);
            receiveString = str;
            //BytesToStruct(iar,epSender);
            //reciveText.text = str;
        }

        /// <summary>
        /// 发送数据主函数
        /// </summary>
        /// <param name="dataStr"></param>
        void Send(string dataStr)
        {
            if (!openSend)
                return;
            byte[] SendData = System.Text.Encoding.UTF8.GetBytes(dataStr);
            client.Send(SendData,SendData.Length,endPoint);
        }

        /// <summary>
        /// 关闭Socket
        /// </summary>
        public void SocketQuit()
        {
            if (serverSocket != null)
            {
                serverSocket.Close();
            }
        }

        /// <summary>
        /// 应用关闭时关闭Socket
        /// </summary>
        private void OnApplicationQuit()
        {
            SocketQuit();
        }

        /// <summary>
        /// 当关闭此对象时关闭Socket
        /// </summary>
        private void OnDisable()
        {
            SocketQuit();
        }

        /// <summary>
        /// 接收UDP控制函数
        /// </summary>
        public void UdpControl()
        {
            if (isCanReceive)
            {
                reciveText.text = receiveString;
                //BytesToStruct(receiveString);
                if (receiveString == ToGameString)
                {
                    //SceneManager.LoadScene("Loading");
                    //gameControl.BeginGame();
                    isCanReceive = false;
                }
            }
        }

    }
}

5.为了便于查看Udp客户端传来的数据,需要在场景中建立如下所示Text,用于显示udp客户端传进来的字符串:

unity udp发送16禁止 unity接收udp_System_03


6.将刚刚建立的text组件拖拽到挂载着udpServer的物体上,如下图所示:

unity udp发送16禁止 unity接收udp_unity udp发送16禁止_04


7.打开网络测试工具,选择Udp客户端,配置如下图所示:

unity udp发送16禁止 unity接收udp_udp_05


8.运行工程,在网络测试工具中发送信息,可以看到服务器端收到了信息,如下图所示:

unity udp发送16禁止 unity接收udp_网络协议_06