当前位置: 首页 > news >正文

Unity-TCP-网络聊天功能(二): 登录与注册

5.客户端实现注册与登录接口

创建好UI

接下来定义发给客户端的协议等

public class MessageHelper
{
	//发送登录的消息给服务器 1002
    public void SendLoginMsg(string account, string pwd)
    {
        LoginMsgC2S msg = new LoginMsgC2S();
        msg.account = account;
        msg.password = pwd;
        var str = JsonHelper.ToJson(msg);//json格式化注册请求
        SendToServer(1002, str);//发送
    }

    public Action<LoginMsgS2C> loginHandle;
    //处理登录(结果)请求
    private void LoginMsgHandle(byte[] obj)
    {
        var str = Encoding.UTF8.GetString(obj);//将接受的消息从字节转换为string
        LoginMsgS2C msg = JsonHelper.ToObject<LoginMsgS2C>(str);//在从json格式的string转为对应LoginMsgS2C
        loginHandle?.Invoke(msg);//如果处理注册消息的事件有订阅,就执行。
    }
    
    //发送聊天信息给服务器
    public void SendChatMsg(string account, string chat)
    {
        ChatMsgC2S msg = new ChatMsgC2S();
        msg.player = account;
        msg.msg = chat;
        msg.type = 0;
        var str = JsonHelper.ToJson(msg);//json格式化注册请求
        SendToServer(1003, str);//发送
    }

    public Action<ChatMsgS2C> chatHandle;
    //处理聊天(转发)请求
    private void ChatMsgHandle(byte[] obj)
    {
        var str = Encoding.UTF8.GetString(obj);//将接受的消息从字节转换为string
        ChatMsgS2C msg = JsonHelper.ToObject<ChatMsgS2C>(str);//在从json格式的string转为对应ChatMsgS2C
        chatHandle?.Invoke(msg);//如果处理注册消息的事件有订阅,就执行。
    }
    
    //发送注册的消息给服务器 1001
    public void SendRegisterMsg(string account, string email, string pwd)
    {
        RegisterMsgC2S msg = new RegisterMsgC2S();
        msg.account = account;
        msg.email = email;
        msg.password = pwd;
        var str = JsonHelper.ToJson(msg);//json格式化注册请求
        SendToServer(1001, str);//发送
    }

    public Action<RegisterMsgS2C> registerHandle;
    //处理注册(结果)请求
    private void RigisterMsgHandle(byte[] obj)
    {
        var str = Encoding.UTF8.GetString(obj);//将接受的消息从字节转换为string
        RegisterMsgS2C msg = JsonHelper.ToObject<RegisterMsgS2C>(str);//在从json格式的string转为对应RegisterMsgS2C
        registerHandle?.Invoke(msg);//如果处理注册消息的事件有订阅,就执行。
    }
}

//1002
public class LoginMsgC2S
{
    public string account;
    public string password;
}

public class LoginMsgS2C
{
    public string account;
    public string password;
    public int result;//0成功 1失败:账号或密码错误
}

//1001
public class RegisterMsgC2S
{
    public string account;
    public string email;
    public string password;
}

public class RegisterMsgS2C
{
    public string account;
    public string email;
    public string password;
    public int result;//0成功 1失败:已被注册的账号
}

//1003
public class ChatMsgC2S
{
    public string player;
    public string msg;
    public int type;//0世界聊天
}

//服务器转发给所有在线的客户端
public class ChatMsgS2C
{
    public string player;
    public string msg;
    public int type;//0世界聊天
}

创建Unity-Scripts/View/LoginView.cs和ChatView.cs

逻辑:LoginView用于UI界面按钮控制,调用MessageHelper里面的方法,发送和处理消息

public class LoginView : MonoBehaviour
{
    private GameObject register;
    
    //注册相关
    private Button registerBtn;
    private InputField usernameInput_Register;
    private InputField emailInput_Register;
    private InputField passwordInput_Register;
    private Button registerBtn_Register;
    private Button closeBtn_Register;
    
    //登录相关
    private Button loginBtn;
    private InputField usernameInput_Login;
    private InputField passwordInput_Login;
    
    // Start is called before the first frame update
    void Start()
    {
        register = transform.Find("Register").gameObject;

        //打开注册界面
        registerBtn = transform.Find("RegisterBtn").GetComponent<Button>();
        registerBtn.onClick.AddListener(OpenRegisterView);
        //关闭注册界面
        closeBtn_Register = transform.Find("Register/CloseBtn").GetComponent<Button>();
        closeBtn_Register.onClick.AddListener(CloseRegisterView);

        //注册界面输入框
        usernameInput_Register = transform.Find("Register/UsernameInput/InputField").GetComponent<InputField>();
        emailInput_Register = transform.Find("Register/EmailInput/InputField").GetComponent<InputField>();
        passwordInput_Register = transform.Find("Register/PasswordInput/InputField").GetComponent<InputField>();

        //注册界面-注册按钮
        registerBtn_Register = transform.Find("Register/RegisterBtn").GetComponent<Button>();
        registerBtn_Register.onClick.AddListener(RegisterOnClick);
        
        //登录界面-登录按钮
        loginBtn = transform.Find("LoginBtn").GetComponent<Button>();
        loginBtn.onClick.AddListener(LoginOnClick);

        usernameInput_Login = transform.Find("UsernameInput/InputField").GetComponent<InputField>();
        passwordInput_Login = transform.Find("PasswordInput/InputField").GetComponent<InputField>();
        
        //处理登录结果
        MessageHelper.Instance.loginHandle += LoginHandle;
        MessageHelper.Instance.registerHandle += RegisterHandle;
    }


    // Update is called once per frame
    void Update()
    {
        
    }

    private void CloseRegisterView()
    {
        register.gameObject.SetActive(false);
    }

    private void OpenRegisterView()
    {
        register.gameObject.SetActive(true);
    }

    private void LoginHandle(LoginMsgS2C obj)
    {
        if (obj.result == 0)
        {
            //登录成功将用户信息和返回的用户信息绑定起来,使用PlayerData类
            PlayerData.Instance.LoginMsgS2C = obj;
            Debug.Log("登陆成功");
            //this.gameObject.SetActive(false);
            Destroy(this.gameObject);
            var chatView = Resources.Load<GameObject>("ChatView");
            var chatObj = GameObject.Instantiate<GameObject>(chatView);
            chatObj.AddComponent<ChatView>();
        }
        else
        {
            Tips($"登录失败,错误码:{obj.result}");
        }
    }

    private void RegisterHandle(RegisterMsgS2C obj)
    {
        if (obj.result == 0)
        {
            Debug.Log("注册成功");
            Tips("注册成功");
            register.gameObject.SetActive(false);
            usernameInput_Login.text = obj.account;
            passwordInput_Login.text = obj.password;
        }
        else
        {
            Tips($"注册失败,错误码:{obj.result}");
        }
    }

    private void LoginOnClick()
    {
        if (string.IsNullOrEmpty(usernameInput_Login.text))
        {
            Tips("请输入账号!!!");
            return;
        }
        if (string.IsNullOrEmpty(passwordInput_Login.text))
        {
            Tips("请输入邮箱!!!");
            return;
        }
        //发送数据给服务器
        MessageHelper.Instance.SendLoginMsg(usernameInput_Login.text, passwordInput_Login.text);
    }

    private void RegisterOnClick()
    {
        if (string.IsNullOrEmpty(usernameInput_Register.text))
        {
            Tips("请输入账号!!!");
            return;
        }
        if (string.IsNullOrEmpty(emailInput_Register.text))
        {
            Tips("请输入邮箱!!!");
            return;
        }
        if (string.IsNullOrEmpty(passwordInput_Register.text))
        {
            Tips("请输入密码!!!");
            return;
        }
        //发送数据给服务器
        MessageHelper.Instance.SendRegisterMsg(usernameInput_Register.text, emailInput_Register.text, passwordInput_Register.text);
    }

    private GameObject _tipsObj;
    public void Tips(string str)
    {
        if (_tipsObj != null)
        {
            GameObject.Destroy(_tipsObj);
        }

        var tipsItem = transform.Find("TipsItem").gameObject;
        _tipsObj = GameObject.Instantiate<GameObject>(tipsItem);
        _tipsObj.transform.SetParent(this.transform, false);
        var content = _tipsObj.transform.Find("Text").GetComponent<Text>();
        content.text = str;
        _tipsObj.gameObject.SetActive(true);
        GameObject.Destroy(_tipsObj, 1.5f);
    }

	private void OnDestroy()
    {
        MessageHelper.Instance.loginHandle -= LoginHandle;
        MessageHelper.Instance.registerHandle -= RegisterHandle;
    }
}

创建Unity-Scripts/PlayerData.cs

public class PlayerData
{
    private static PlayerData instance = new PlayerData();
    public static PlayerData Instance => instance;

    public LoginMsgS2C LoginMsgS2C;
}

测试运行。

6.服务端处理注册与登录业务

将Unity MessageHelper里的消息类C2S、S2C等复制到Server-MessageHelper中

class MessageHelper
{
}

//1002
public class LoginMsgC2S
{
    public string account;
    public string password;
}

public class LoginMsgS2C
{
    public string account;
    public string password;
    public int result;//0成功 1失败:账号或密码错误
}

//1001
public class RegisterMsgC2S
{
    public string account;
    public string email;
    public string password;
}

public class RegisterMsgS2C
{
    public string account;
    public string email;
    public string password;
    public int result;//0成功 1失败:已被注册的账号
}

//1003
public class ChatMsgC2S
{
    public string player;
    public string msg;
    public int type;//0世界聊天
}

//服务器转发给所有在线的客户端
public class ChatMsgS2C
{
    public string player;
    public string msg;
    public int type;//0世界聊天
}

Server-Client理论上需要进入数据库判断有无账号,然后处理业务,这里用一个类Server/PlayerData.cs缓存这些数据,不用数据库了

namespace Server
{
    internal class PlayerData
    {
        private static PlayerData instance = new PlayerData();
        public static PlayerData Instance => instance;//单例
        Dictionary<string, RegisterMsgS2C> userMsg = new Dictionary<string, RegisterMsgS2C>();//缓存注册信息

        //注册后,result=0表示注册成功,将这个注册后的信息返回给客户端
        public RegisterMsgS2C Add(RegisterMsgC2S msgC2S)
        {
            var item = new RegisterMsgS2C();
            userMsg[msgC2S.account] = item;
            item.account = msgC2S.account;
            item.password = msgC2S.password;
            item.result = 0;
            return item;
        }

        //判断是否存在相同的账号
        public bool Contain(string account)
        {
            return userMsg.ContainsKey(account);
        }

        //维护已经登陆的用户
        Dictionary<string, Client> LoginUser = new Dictionary<string, Client>();
        public void AddLoginUser(string account, Client client)
        {
            LoginUser[account] = client;
        }

        public Dictionary<string, Client> GetAllLoginUser()
        {
            return LoginUser;
        }
    }
}
//处理注册请求
private void RegisterMsgHandle(byte[] obj)
{
    var msg = JsonHelper.ToObject<RegisterMsgC2S>(obj);
    RegisterMsgS2C response = null;
    if (PlayerData.Instance.Contain(msg.account))
    {
        response = new RegisterMsgS2C();
        response.result = 1;//已经包含该帐号了
    }
    else
    {
        response = PlayerData.Instance.Add(msg);
    }
    SendToClient(1001, JsonHelper.ToJson(response));
}

//处理登录请求
private void LoginMsgHandle(byte[] obj)
{
    //判断账号和密码是否跟缓存的一直,如果是,返回账号和密码
    var msg = JsonHelper.ToObject<LoginMsgC2S>(obj);
    LoginMsgS2C response = new LoginMsgS2C();
    if (PlayerData.Instance.Contain(msg.account))
    {
        response.result = 0;//已包含该账号
        response.account = msg.account;
        response.password = msg.password;

        //将用户储存起来
        PlayerData.Instance.AddLoginUser(msg.account, this);
    }
    else
    {
        response.result = 1;//账号或密码错误
    }
    SendToClient(1002, JsonHelper.ToJson(response));
}

运行测试,表明服务器收到了Unity客户端的注册信息后,在PlayerData中检测没有账号后,注册了一条数据,并返回给Unity客户端注册成功

再次注册同样的数据,表示注册失败,返回result = 1

用刚刚的账号登录,就登录成功了

为什么Unity和Server的输出都是乱码呢,因为直接输出的是包体大小+协议id+包体,所以前面是乱码,只输出包体就不会乱码,一下为修改了在send和receive时只输出body内容,输出内容正常。

相关文章:

  • 51单片机最强模块化封装(1)
  • Python学习-----起步2(变量与转义符)
  • Java程序设计实验3 | 面向对象(上)
  • 优秀的代码最终选择if else,还是switch case
  • Openharmony的编译构建--进阶篇1
  • 每天一道大厂SQL题【Day02】电商场景TopK统计
  • EMT4J详细介绍与使用,帮你找到Java版本升级带来的问题,让你在项目jdk升级不在头疼
  • 第2章:使用CSS定义样式
  • 【数据结构】动图详解单向链表
  • MySQL基础篇笔记
  • Vue3现状—必然趋势?
  • uniapp获取支付宝user_id - 支付宝提现 - 登录授权 - APP支付宝登陆 - H5支付宝授权
  • Promise详解与手写实现
  • 【C++】类型转换
  • 关于栈和队列
  • 网络知识详解之:网络攻击与安全防护
  • Java快速上手Properties集合类
  • leetcode:43. 字符串相乘(附加一些C++string其他小练习)
  • 游戏SDK(三)架构设计之代码实现1
  • 射频识别技术|期末考试知识点|重点题目|第1讲_RFID
  • https://app.hackthebox.com/machines/Inject
  • Spring —— Spring简单的读取和存储对象 Ⅱ
  • 渗透测试之冰蝎实战
  • Mybatis、TKMybatis对比
  • Microsoft Office 2019(2022年10月批量许可版)图文教程
  • 《谷粒商城基础篇》分布式基础环境搭建
  • 哈希表题目:砖墙
  • Vue 3.0 选项 生命周期钩子
  • 【车载嵌入式开发】AutoSar架构入门介绍篇
  • 【计算机视觉 | 目标检测】DETR风格的目标检测框架解读