zoukankan      html  css  js  c++  java
  • 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(客户端收发消息)
  • 相关阅读:
    Luogu_P3435 [POI2006]OKR-Periods of Words KMP
    NOIP2019游记
    []記錄容易出錯的地方和一些知識
    [题解]luogu_P3939_数颜色(vector二分
    [题解]luogu_P4819_杀人游戏(缩点
    [题解]NOIP2018_赛道修建(二分/树形dp/set/贪心
    [题解]luogu_P4161_(排列/lcm
    [题解]luogu_P4886_快递员(点分治
    [题解]luogu_P3523(树上覆盖
    [题解]luogu_P2466(区间dp
  • 原文地址:https://www.cnblogs.com/JimmyCode/p/6499050.html
Copyright © 2011-2022 走看看