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内容,输出内容正常。