前言:

我们在完成服务端的搭建与客户端连接客户端的功能后,需要实现两者之间的通信功能

本次制作Unity登录界面来学习使用Unity服务端与客户端进行数据传输

客户端:

UI搭建

首先就是添加两个输入框,在Hierarchy面板添加两个输入框Input Field,分别命名为UserName和Password,来接受数据的传入。添加后修改子元素Placeholder中的Text组件中的文字可以完成提示文字的修改

然后使用添加Button作为数据提交按钮,点击后作为登录验证按钮

效果图:

java做unity的服务端 unity 服务端_字符串


注意事项:

为了可以保证输入格式符合标准,可以在Input Field中选择Content Type中选择合适的格式,来限制用户的输入格式,账号选择Name,而密码则可以选择Password

java做unity的服务端 unity 服务端_java做unity的服务端_02

脚本


脚本思路:

  • 首先获取登录的账号密码,这里使用InputField.text方法来获取输入框输入的内容,然后对输入的内容进行判断是否满足条件,如果满足条件者将账号密码两个内容合并
  • 然后连接服务器,将合并的数据转换为字节流发送给服务器
  • 获取客户端的返回结果,如果存在账号密码,则登录成功,如果账号密码错误,则登录失败,重新执行上面的步骤

第一步,定义处理输入数据方法

在按钮提交时,首先要确保两个输入框的数据不为空,然后将两个字符串合并为一个并返回为字符串,这样方便传输

我们定义一个函数getInputDate()来实现这个方法,最终返回的为账号密码的合并字符串(中间用空格隔开),但是在返回字符串之前要判断两个输入框是否为空,如果为空,则返回为null,不为空则返回合并后的字符串:

//账号密码输入框
    public InputField userNameInput;
    public InputField passwordInput;
    /// <summary>
    /// 获取输入框内容
    /// </summary>
    public string getInputDate()
    {
        if (userNameInput.text != "" && passwordInput.text != "")
        {
            //获取输入框文本,并剔除掉空格
            string userName = userNameInput.text.Replace(" ","");
            string password = passwordInput.text.Replace(" ", "");
            //将两个字符串合并
            string strSend = userName + " " + password;
            //测试
            Debug.Log("合并字符串为:" + strSend);
            return strSend;
        }
        return null;
    }

第二步,定义将输入发送给服务器端方法

这一步是客户端与服务器端连接的关键,首先使用之前的连接服务器类来创建一个连接,然后将数据发送给服务器端

首先定义一个函数,将上一步执行返回的字符串(不为空的情况下)为参数,我们要实现在第一次调用该函数时,使用Socket套接字将客户端连接到服务器,为了实现仅仅第一次调用连接,使用if语句进行判断,然后将获取的字符串发送给服务器:

//ConnServer 为Unity网络编程二中连接服务器定义的类(可以查看该文章获取源码)
    private ConnServer connServer;
    private Socket socket;
    private bool isConn = false;

    /// <summary>
    /// 向服务器发送数据
    /// </summary>
    /// <param name="strSend"></param>
    public Socket sendDateToServer(string strSend)
    {   
        byte[] bytes=Encoding.UTF8.GetBytes(strSend);
        //第一次调用该方法时创建一个连接socket
        if (isConn==false)
        {
            connServer = new ConnServer("127.0.0.1", 30000);           
            socket = connServer.conn();            
            isConn = true;            
        }       
        //向服务器发送数据
        socket.Send(bytes);
        Debug.Log("数据发送成功");
        return socket;
    }

第三步,接收数据库处理完数据之后的返回值

当我们发送非空的账号密码给服务器后,服务器会执行数据库查询,查询的状态有下面几种,然后通过字符串代表这些支付串(我们实际应用可以用更短的方式传输),返回给客户端:

  • succes: 查询账号密码存在
  • userNameError:查询账号不存在,即用户名错误
  • passwordError:查询账号存在,但是密码不存在,即密码错误
  • error:这种情况代表未知错误,统一写为服务器连接错误

我们定义的方法,就是为了接受这样的字符串具体实现方式为:

定义参数,接受之前与服务器建立连接的socket,然后在方法中,使用接受方法Receive()接受传回来的字节流,并将其转换为字符串返回。使用try catch监控错误,如果过程出错,返回error,统一按照服务器连接错误处理:

/// <summary>
    ///  获取服务器的返回结果
    /// </summary>
    /// <param name="socket"></param>
    /// <returns></returns>
    public string getReturnDate(Socket socketDemo)
    {
        byte[] bytes;
        string strGet = null;
        int maxBuffer = 1024;
        byte[] buffer = new byte[maxBuffer];
        try
        {
            int length = socketDemo.Receive(buffer);
            strGet = Encoding.UTF8.GetString(buffer, 0, length);
            Debug.Log("收到服务器传回内容:" + strGet);
            return strGet;
        }
        catch (Exception e)
        {

            Debug.Log(" 获取服务器内容失败:错误为:" + e.ToString());
        }
        return "error";
    }

第四步,定义处理服务器返回结果的方法

我们在第三步接受了服务器返回的结果,则需要对其进行处理分析,并做出对应的反应状态,具体反应为:

  • 接受到succes:场景跳转
  • 接收到userNameError:提示"输入账号错误"
  • 接收到passwordError:提示"输入密码错误"
  • 接收到error:提示"服务器连接错误,请重新尝试"

但是为了测试,我们统一打印日志来显示查询结果:

public void showStatus(string strGet)
    {
        switch(strGet)
        {
            case "succes":

                Debug.Log("查询成功");
                break;
            case "userNameError":
                Debug.Log("输入用户名错误");
                break;
            case "passwordError":
                Debug.Log("输入密码错误");
                break;
            default:
                Debug.Log("服务器连接错误");
                break;
        }
    }

第五步,定义提交按钮监控事件方法

这一步的含义就是写一个绑定在提交按钮上的方法,并将前面的方法执行,完成登录系统的所有客户端步骤

public void loginButton()
    {
        //获取连个输入框输入并合并返回一个字符串
        string strSend = getInputDate();
        if (strSend != null)
        {
            //如果返回的字符串不为空,则将其发送给服务器端
            Socket socket = sendDateToServer(strSend);
            //接受服务器端返回的结果
            string strReturn = getReturnDate(socket);
            //处理服务端返回的结果
            showStatus(strReturn);
        }
        else
        {
            Debug.Log("账号或密码为空");
        }
    }

第六步登录脚本绑定登录按钮

第一,首先将脚本添加到场景中的物体上,本测试选择新建一个空物体命名为Scripts,然后将脚本拖入到该物体

第二,接下来需要给脚本拖入需要的两个输入框:

java做unity的服务端 unity 服务端_java做unity的服务端_03

第三,为按钮绑定对应loginButton()方法,这样就完成了客户端的程序设计

java做unity的服务端 unity 服务端_java做unity的服务端_04


服务器端

服务器端创建一个脚本也命名为Login,做为实现登录功能的服务器端的脚本

第一步,获取客户端发送过来的账号密码

首先创建一个函数GetReceive()做为一个获取客户端传来数据的接受方法,并定义一个参数socket作为连接到服务器的一个Socket的监听方法

首先使用SocketReceive() 获取字节流,并通过一定方法转换为字符串,最后返回

/// <summary>
        /// 获取客户端发送来的账号密码
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public string GetReceive(Socket socket)
        {
            string strGet = null;
            int maxBuffer = 1024;
            byte[] buffer = new byte[maxBuffer];
            try
            {                
                int length = socket.Receive(buffer);             
                strGet = Encoding.UTF8.GetString(buffer, 0, length);
                Console.WriteLine("收到内容:"+strGet);
                return strGet;               
            }
            catch (Exception e)
            {
                Console.WriteLine(" 获取内容失败:错误为:" + e.ToString());
            }
            return null;
        }

第二步,查询数据库账号密码

数据库的查询首先需要连接数据库,在"Unity网络编程二"中些了关于数据库的连接类ConnDB,这里会字节调用,如果不理解,可以查看之前的文章

  • 首先创建函数FindDate(),并接受字符串参数,即从客户端接受的参数,接受后,首先需要对该字符串进行处理,因为该字符串是由账号密码合并而成,需要使用Split()方法将其分解
  • 获取账号后,通过MySqlCommand()执行sql命令,通过账号完成查询,并通过ExecuteReader()完成对于查询数据的获取,最终获取查询后的密码
  • 通过系列判断,以及查询的密码和客户端传来的密码进行对比,然后得出结果,这样就可以得到系列的结果(用字符串表示),然后返回

而数据库中Login表的结构为:

java做unity的服务端 unity 服务端_服务器_05

具体的代码为:

//输入连接字符串需要的参数
 		private string hostDB = "localhost";
        private string portDB = "3306";
        private string userDB = "root";
        private string passwordDB = ""; //数据库密码未作展示
        private string dateNameDB = "day05";

        /// <summary>
        /// 查询数据库,判断字符串是否存在
        /// </summary>
        /// <param name="strGet"></param>
        /// <returns></returns>
        private string FindDate(string strGet)
        {
            if(strGet==null)
            {
                Console.WriteLine("获取服务器字符串为空");
                return "null";
            }

            //处理输入的账号密码字符串
            string[] strGets = strGet.Split(" ");
            string userName = strGets[0];
            string password = strGets[1];

            //创建sql语言并执行查询
            try
            {
                
                ConnDB connDB = new ConnDB(hostDB, portDB, userDB, passwordDB, dateNameDB);
                MySqlConnection conn = connDB.openDate();

                string sql = string.Format("select * from Login where username=\"{0}\"", userName);
                MySqlCommand cmd = new MySqlCommand(sql, conn);

                //对查询的返回结果进行读取
                MySqlDataReader reader = cmd.ExecuteReader();
                if (reader.Read()) //判断查询结果是否为空
                {
                    if(reader[0].Equals(password))
                    {
                        Console.WriteLine("成功查询到账号密码");
                        return "succes";
                    }
                    else
                    {
                        Console.WriteLine("有查询结果,但是与输入不同,判短输入密码错误");
                        return "passwordError";
                    }
                }
                else
                {
                    Console.WriteLine("查询为空,账号不存在");
                    return "userNameError";
                }               
            }
            catch (Exception e)
            {

                Console.WriteLine("连接数据库报错:"+e.ToString());
            }
            Console.WriteLine("查询过程出错");
            return "error";
        }

第三步,向客户端返回结果

这一步这是将刚刚数据库查询完的结果返回给连接进来的客户端,代码简单,直接观看即可:

/// <summary>
        /// 向客户端返回查询结果
        /// </summary>
        private void SendDate(Socket socket,string strSend)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(strSend);
            socket.Send(bytes);
        }

第四步,在函数的构造函数中调取方法

首先需要创建一个socket,作为服务器的Socket,这里世界使用之前Unity网络编程一里面的创建服务器的类来创建一个Socket,并启动

完成启动后,则需要使用Accept() 监控连接进行的socket, 并使用循环监控客户端发来的数据并进行系列处理后返回:

//创建socket连接所用服务器地址和端口号
        private string ip = "127.0.0.1";
        private int port = 30000;
        /// <summary>
        /// 方便在实例化时直接执行相关操作
        /// </summary>
        public LogIn()
        {
            CreateServer server = new CreateServer(ip, port);
            Socket socket = server.StartSocket();
            Socket clider = socket.Accept();

            while (true)
            {              
                string strGet = GetReceive(clider);              
                string strSend = FindDate(strGet);
                Console.WriteLine(strSend);
                SendDate(clider, strSend);
            }
		}

第五步,在主函数中实例化该登录类,并进行测试

我们在Main()函数中直接实例化login即可,然后启动该项目,同时在Unity客户端中启动项目,然后输入账号密码进行测试,判断程序运行是否有误

总结

通过Socket来实现前后端的数据交互需要在客户端和服务器端分别完成数据的发送和接受,同时客户端处理客户端的逻辑,而服务端处理服务端的逻辑,这样的优势是减小数据的传输量