Netty实现Unity登录验证(四)

这里我们用Netty实现消息的接受及分发,服务器端的 handler 添加流程如图:

 1 package com.netty.dispatch;
 2 
 3 import com.netty.model.SocketModel;
 4 import com.netty.protocol.LoginProtocol;
 5 import io.netty.channel.ChannelHandlerContext;
 6 
 7 public class BattleDispatch {
 8     private static BattleDispatch instance = new BattleDispatch();
 9 
10     public static BattleDispatch getInstance() {
11         return instance;
12     }
13 
14     public void dispatch(ChannelHandlerContext ctx, SocketModel message) {
15         switch (message.getArea()) {
16         case LoginProtocol.Area_LoginRequest:
17             // 处理登录的事务
18             break;
19         default:
20             break;
21         }
22     }
23 }
BattleDispatch(服务端处理战斗事件,并把消息分发给客户端)
 1 package com.netty.dispatch;
 2 
 3 /*
 4  * 处理登陆数据并把结果分发回客户端
 5  */
 6 
 7 import java.util.List;
 8 import com.netty.model.SocketModel;
 9 import com.netty.protocol.LoginProtocol;
10 import com.netty.protocol.TypeProtocol;
11 import com.netty.util.VerifyOperate;
12 
13 import io.netty.channel.ChannelHandlerContext;
14 
15 public class LoginDispatch {
16 
17   private static LoginDispatch instance = new LoginDispatch();
18 
19   public static LoginDispatch getInstance() {
20     return instance;
21   }
22 
23   public void dispatch(ChannelHandlerContext ctx, SocketModel message) {
24     switch (message.getArea()) {
25     case LoginProtocol.Area_LoginRequest:
26       LoginResponse(ctx, message);
27 
28       break;
29     default:
30       break;
31     }
32   }
33   /*
34   *检测用户登录是否密码错误,用户名不存在等,返回int对应的不同类型
35   */
36   public void LoginResponse(ChannelHandlerContext ctx, SocketModel request) {
37     SocketModel response = new SocketModel();
38     int command = LoginCheck(ctx, request);               //success 10
39     response.setType(TypeProtocol.TYPE_LOGIN);            //0
40     response.setArea(LoginProtocol.Area_LoginResponse);   //6
41     response.setCommand(command);
42     response.setMessage(request.getMessage());
43     ctx.writeAndFlush(response);                          //
44 
45     if (command == LoginProtocol.Login_Succeed) {
46       LoginUser(ctx, request);// 如果成功就登陆用户,开始新手向导
47     }
48   }
49 
50   public int LoginCheck(ChannelHandlerContext ctx, SocketModel request) {
51     List<String> message = request.getMessage();
52     String username = message.get(0);
53     String password = message.get(1);
54 
55     System.out.println(username);
56     System.out.println(password);
57 
58     if (message.isEmpty()) {
59       return LoginProtocol.Login_InvalidMessage;
60     } else {
61       if (VerifyOperate.getInstance().checkPlayer("tb_verify", username,
62           password) > 1000) {
63         return LoginProtocol.Login_Succeed;
64       } else {
65         return LoginProtocol.Login_InvalidPassword;
66       }
67     }
68   }
69 
70   public void LoginUser(ChannelHandlerContext ctx, SocketModel socketModel) {
71     // user =
72     // UserMySQL.getInstance().initUser(User.getUserByChannel(ctx.channel()));
73     // user.setWizard(WizardMySQL.getInstance().initWizard(user.getUserID()));
74     // SocketModel message = new SocketModel();
75     // message.setType(TypeProtocol.TYPE_WIZARD);
76     // message.setArea(WizardProtocol.Wizard_Create_Request);
77     // message.setCommand(user.getWizard().getStepIndex());
78     // message.setMessage(null);
79     // ctx.writeAndFlush(message);
80   }
81 }
LoginDispatch(服务端处理登陆事件,并把消息分发给客户端)
 1 package com.netty.server;
 2 
 3 import com.netty.dispatch.BattleDispatch;
 4 import com.netty.dispatch.LoginDispatch;
 5 import com.netty.model.SocketModel;
 6 import com.netty.protocol.TypeProtocol;
 7 import io.netty.channel.ChannelHandlerAdapter;
 8 import io.netty.channel.ChannelHandlerContext;
 9 
10 public class ServerHandler extends ChannelHandlerAdapter {
11 
12     public void channelActive(ChannelHandlerContext ctx) throws Exception // 客户端连上服务器的时候会触发
13     {
14         System.out.println("client:" + ctx.channel().id() + " join server");
15     }
16 
17     public void channelInactive(ChannelHandlerContext ctx) throws Exception // 当客户端断开连接的时候触发
18     {
19         System.out.println("client:" + ctx.channel().id() + " leave server");
20     }
21 
22     public void channelRead(ChannelHandlerContext ctx, Object msg)
23             throws Exception // 当客户端发送数据到服务器会触发
24     {
25         SocketModel message = (SocketModel) msg;
26 
27         switch (message.getType()) {
28         case TypeProtocol.TYPE_LOGIN:
29             LoginDispatch.getInstance().dispatch(ctx, message);
30             break;
31             
32         case TypeProtocol.TYPE_USER:
33             //UserDispatch.getInstance().dispatch(ctx, message);
34             break;
35             
36         case TypeProtocol.TYPE_WIZARD:
37             //WizardDispatch.getInstance().dispatch(ctx, message);
38             break;
39             
40         case TypeProtocol.TYPE_BATTLE:
41             BattleDispatch.getInstance().dispatch(ctx, message);
42             break;
43             
44         default:
45             break;
46         }
47 
48     }
49 
50     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
51             throws Exception {
52 
53     }
54 }
ServerHandler(服务端业务事件手柄)
 1 package com.netty.server;
 2 
 3 import com.netty.decoder.LengthDecoder;
 4 import com.netty.decoder.MessageDecoder;
 5 import com.netty.encoder.MessageEncoder;
 6 
 7 import io.netty.bootstrap.ServerBootstrap;
 8 import io.netty.channel.ChannelFuture;
 9 import io.netty.channel.ChannelInitializer;
10 import io.netty.channel.ChannelOption;
11 import io.netty.channel.EventLoopGroup;
12 import io.netty.channel.nio.NioEventLoopGroup;
13 import io.netty.channel.socket.SocketChannel;
14 import io.netty.channel.socket.nio.NioServerSocketChannel;
15 
16 public class GameServer {
17   
18   public void bind(int port) throws Exception
19   {
20     //Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,
21     //注册Channel,管理他们的生命周期 
22     //服务器端需要指定两个 EventLoopGroup, 一个是 bossGroup, 用于处理客户端的连接请求; 
23     //另一个是 workerGroup, 用于处理与各个客户端连接的 IO 操作.
24     EventLoopGroup bossGroup = new NioEventLoopGroup();
25     EventLoopGroup workGroup = new NioEventLoopGroup();
26     try
27     {
28       ServerBootstrap b = new ServerBootstrap();   //server 引导程序
29       
30       b.group(bossGroup, workGroup)
31       .channel(NioServerSocketChannel.class)    //指定 Channel类型. 因为是服务器端使用NioServerSocketChannel.
32       .option(ChannelOption.SO_BACKLOG, 1024)   //客户端连接数为1024
33       .childHandler(new ChannelInitializer<SocketChannel>()
34           {
35             protected void initChannel(SocketChannel ch) throws Exception
36             {
37               //注册业务handler
38               ch.pipeline().addLast(new LengthDecoder(1024, 0, 4, 0, 4));
39               ch.pipeline().addLast(new MessageDecoder());
40               ch.pipeline().addLast(new MessageEncoder());
41               ch.pipeline().addLast(new ServerHandler());
42             }
43           });
44       
45       ChannelFuture f = b.bind(port).sync();
46       
47       if(f.isSuccess())
48       {
49         System.out.println("Server starts success at port: " + port);
50       }
51       
52       f.channel().closeFuture().sync();
53     }
54     catch(Exception e)
55     {
56       e.printStackTrace();
57     }
58     finally
59     {
60       bossGroup.shutdownGracefully();
61       workGroup.shutdownGracefully();
62     }
63     
64   }
65   
66   public static void main(String args[]) throws Exception 
67   {
68     int port = 8080;
69     new GameServer().bind(port);
70   }
71 }
GameServer(服务端程序入口)

我们添加客户端登陆逻辑

 1 using UnityEngine;
 2 using System.Collections.Generic;
 3 using UnityEngine.UI;
 4 
 5 public class LoginUI : MonoBehaviour {
 6 
 7     private InputField UserNameInput;
 8     private InputField PassWordInput;
 9 
10     void Start()
11     {
12         UserNameInput = transform.Find("UserName").GetComponent<InputField>();
13         PassWordInput = transform.Find("PassWord").GetComponent<InputField>();
14 
15         if (PlayerPrefs.GetString("Username") != null && PlayerPrefs.GetString("Username") != "")
16         {
17             UserNameInput.text = PlayerPrefs.GetString("Username");  //数据持久化
18         }
19     }
20 
21     public void ButtonClick(GameObject click)
22     {
23         if(click.name.Equals("BtnLogin"))
24         {
25             if (UserNameInput.text == "")
26             {
27                 return;
28             }
29             else if (PassWordInput.text == "")
30             {
31                 return;
32             }
33             else
34             {
35                 SocketModel LoginRequest = new SocketModel();
36                 LoginRequest.SetType(TypeProtocol.TYPE_LOGIN);
37                 LoginRequest.SetArea(LoginProtocol.Area_LoginRequest);
38                 LoginRequest.SetCommand(0);
39                 List<string> message = new List<string>();
40                 message.Add(UserNameInput.text);
41                 message.Add(PassWordInput.text);
42                 LoginRequest.SetMessage(message);
43                 MainClient.instance.SendMsg(LoginRequest);
44             }
45         }
46         if (click.name.Equals("BtnRegister"))
47         {
48             //用户注册
49         }
50     }    
51 }
LoginUI(客户端登陆UI监听)

最后新建一个gameobject,将MainClient赋给它,将之做成prefab

  1 using UnityEngine;
  2 using System.Net.Sockets;
  3 using System;
  4 using System.IO;
  5 using ProtoBuf;
  6 
  7 public class MainClient : MonoBehaviour {
  8 
  9     private const string HOST = "192.168.5.141";  //服务器IP
 10     private const int PORT = 8080;
 11     public static MainClient instance;
 12     public static TcpClient client;
 13 
 14     private byte[] receiveData;
 15     private int len;     //消息长度
 16     private bool isHead;
 17     private SocketModel message;
 18 
 19     void Awake()
 20     {
 21         if (instance == null)
 22         {
 23             instance = this;
 24             DontDestroyOnLoad(this.gameObject);
 25         }
 26     }
 27 
 28     // Use this for initialization
 29     void Start ()
 30     {
 31         if (client == null)
 32         {
 33             Connect();
 34         }
 35 
 36         isHead = true;
 37         receiveData = new byte[800];
 38         ///在start里面开始异步接收消息
 39         client.GetStream().BeginRead(receiveData, 0, 800, ReceiveMsg, client.GetStream());
 40     }
 41 
 42     public void SendMsg(SocketModel socketModel)
 43     {
 44         byte[] msg = Serial(socketModel);
 45         //消息体结构:消息体长度+消息体
 46         byte[] data = new byte[4 + msg.Length];
 47         IntToBytes(msg.Length).CopyTo(data, 0);
 48         msg.CopyTo(data, 4);
 49         client.GetStream().Write(data, 0, data.Length);
 50     }
 51 
 52     public void ReceiveMsg(IAsyncResult ar)   //异步接收消息
 53     {
 54         NetworkStream stream = (NetworkStream)ar.AsyncState;
 55         stream.EndRead(ar);
 56         
 57         if(isHead)     //读取消息体的长度
 58             {
 59             byte[] lenByte = new byte[4];
 60             System.Array.Copy(receiveData, lenByte, 4);
 61             len = ByteToInt(lenByte, 0);
 62             isHead = false;
 63         }
 64         if(!isHead)    //读取消息体内容
 65         {
 66             byte[] msgByte = new byte[len];
 67             //ConstrainedCopy: sourceArray,int sourceIndex,Array destinationArray,    
 68             //int destinationIndex,int length
 69             System.Array.ConstrainedCopy(receiveData, 4, msgByte, 0, len);  
 70             isHead = true;
 71             len = 0;
 72             message = DeSerial(msgByte);
 73         }
 74 
 75         stream.BeginRead(receiveData, 0, 800, ReceiveMsg, stream);
 76 
 77         switch (message.GetCommand())
 78         {
 79             case LoginProtocol.Login_Succeed:
 80                 Debug.Log("Login_Succeed");
 81                 break;
 82         
 83             case LoginProtocol.Login_InvalidPassword:
 84                 Debug.Log("InvalidPassword");
 85                 break;
 86 
 87             default:
 88                 Debug.Log("TMT");
 89                 break;
 90         }
 91     }
 92 
 93     private byte[] Serial(SocketModel socketModel)  //将SocketModel转化成字节数组
 94     {
 95         using (MemoryStream ms = new MemoryStream())
 96         {
 97             Serializer.Serialize<SocketModel>(ms, socketModel);
 98             byte[] data = new byte[ms.Length];
 99             ms.Position = 0;
100             ms.Read(data, 0, data.Length);
101             return data;
102         }
103     }
104 
105     private SocketModel DeSerial(byte[] msg)  //将字节数组转化成我们的消息类型SocketModel
106     {
107         using (MemoryStream ms = new MemoryStream())
108         {
109             ms.Write(msg, 0, msg.Length);
110             ms.Position = 0;
111             SocketModel socketModel = Serializer.Deserialize<SocketModel>(ms);
112             return socketModel;
113         }
114     }
115 
116     public static int ByteToInt(byte[] data, int offset)
117     {
118         int num = 0;
119         for(int i = offset; i < offset + 4; i++)
120         {
121             num <<= 8;
122             num |= (data[i] & 0xff);
123         }
124         return num;
125     }
126 
127     public static byte[] IntToBytes(int num)
128     {
129         byte[] bytes = new byte[4];
130         for (int i = 0; i < 4; i++)
131         {
132             bytes[i] = (byte)(num >> (24 - i * 8));
133         }
134         return bytes;
135     }
136 
137     void OnApplicatonQuit()
138     {
139         client.Close();
140     }
141 
142     public void Connect()
143     {
144         client = new TcpClient();
145         try
146         {
147             client.Connect(HOST, PORT);
148         }
149         catch(Exception e)
150         {
151             Debug.LogException(e);
152             client.Close();
153         }
154     }
155 
156 }
MainClient(客户端收发消息)

 

转载于:https://www.cnblogs.com/JimmyCode/p/6499050.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值