游戏制作-联网对战丛林战争制作笔记(一)

          开篇先说明,这个游戏制作也是我跟随别人的教程制作的游戏,因此想要了解更多的内容可以去看siki老师的视频,我这里做笔记的目的有两个,一个是帮助喜欢看文字版教程的朋友进一步的学习,一个是保存自己在学习中的一个逻辑和思路,如果看到这个视频会对你有帮助,那就再好不过了,当然这里的话是其中百分之30的内容,后面的制作我会放在我的其他博客下继续

1.    关于IP和端口号的知识

我们经常申请的端口号是49152-65535,1024以前的基本是知名端口

2.    绑定我们的IP和端口号

namespace tcp服务器端

{

   classProgram

    {

        staticvoid Main(string[] args)

        {

            //首先,创建我们的Socket,是ipv4的地址,以流的形式传输,使用的是tcp协议

            Socketmysocket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //第二部,创建我们的ip地址

           IPAddress ipaddress = IPAddress.Parse("127.0.0.1");

            //将ip地址和我们指定的端口号进行绑定

           IPEndPoint ipendpoint = new IPEndPoint(ipaddress, 88);

            //将socket和端口号进行绑定

           mysocket.Bind(ipendpoint);

        }

    }

}

3.  创建我们的服务器端

这里的话,我们让socket开始监听以后,就创建一个接受客户端连接的socket,我们一般是用receive和send来收发数据,这里要注意传递的数据都是字节数组,我们要将它转变成

   classProgram

   {

       staticvoid Main(string[] args)

       {

            //首先,创建我们的Socket,是ipv4的地址,以流的形式传输,使用的是tcp协议

            Socket mysocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);

            //第二部,创建我们的ip地址

            IPAddress ipaddress =IPAddress.Parse("127.0.0.1");

            //将ip地址和我们指定的端口号进行绑定

            IPEndPoint ipendpoint = new IPEndPoint(ipaddress,88);

            //将socket和端口号进行绑定

            mysocket.Bind(ipendpoint);

            mysocket.Listen(0);

            Socket clientSocket =mysocket.Accept();//接受客户端连接

 

            //向客户端发送一条信息

            string msg = "hello客户端";

            byte[] data = System.Text.Encoding.UTF8.GetBytes(msg);

            clientSocket.Send(data);

            //从客户端接受数据

            byte[] getdata = newbyte[1024];

            int count = clientSocket.Receive(getdata);

            //这里表示用getdata这个数组来接受客户端传递的数据,再将传递数据长度的字节数组转换成字符串再输出

            string msg2 = System.Text.Encoding.UTF8.GetString(getdata, 0,count);

            Console.WriteLine(msg2);

            clientSocket.Close();

            mysocket.Close();

 

 

 

 

 

        }

4.    创建我们的客户端

客户端也是一样创建自己的socket,和服务器提供的端口相连接,然后再收发信息即可

5.  using System;

6.  using System.Collections.Generic;

7.  using System.Linq;

8.  using System.Text;

9.  using System.Threading.Tasks;

10.using System.Net.Sockets;

11.using System.Net;

12. 

13.namespace tcp客户端

14.{

15.    classProgram

16.    {

17.        staticvoid Main(string[] args)

18.        {

19.            Socket clientSocket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

20.            IPEndPoint iPEndPoint = newIPEndPoint(IPAddress.Parse("10.128.230.79"), 88);

21.            clientSocket.Connect(iPEndPoint);

22.            //客户端接受输出

23.            byte[] receivedata = newbyte[1024];

24.            int count = clientSocket.Receive(receivedata);

25.            string message = Encoding.UTF8.GetString(receivedata, 0,count);

26.            Console.WriteLine(message);

27.            

28.            //客户端向服务器发送数据

29.            string msg2 = Console.ReadLine();

30.            clientSocket.Send(Encoding.UTF8.GetBytes(msg2));

31.            

32.            Console.ReadKey();

33.            clientSocket.Close();

34. 

35. 

36.        }

37.    }

38.  }

5.如何异步获取到数据

  这里我们使用一个BeginReceive开始异步接受数据,传递过来我们的客户端这个参数,然后重复调用一个AsyncCallBack的方法,通过begin和end来实现多次对于信息的接受

  staticbyte[] databuffer = newbyte[1024];

       clientSocket.BeginReceive(databuffer, 0, 1024, SocketFlags.None,AsyncCallback, clientSocket);

       }

       staticvoidAsyncCallback(IAsyncResult ar){

 

            Socket clientSocket = ar.AsyncStateas Socket;

            int count = clientSocket.EndReceive(ar);

            string msg = Encoding.UTF8.GetString(databuffer, 0, count);

            Console.WriteLine("从客户端接受信息" + msg);

           clientSocket.BeginReceive(databuffer, 0, 1024, SocketFlags.None,AsyncCallback, clientSocket);

 

 

 

        }

当然客户端的话,只要while True发送信息即可

    //客户端向服务器发送数据

            while (true)

            {

                string msg2 = Console.ReadLine();

               clientSocket.Send(Encoding.UTF8.GetBytes(msg2));

            }

6.异步与客户端建立连接

想要同时和多个客户端进行连接,也是使用一个mySocket.BeginAccept和endAccept来进行开始接受连接和结束连接,多次执行即可实现和多个客户端建立连接

   staticvoid StartSocket()

       {

           //首先,创建我们的Socket,是ipv4的地址,以流的形式传输,使用的是tcp协议

            Socket mysocket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //第二部,创建我们的ip地址

            IPAddress ipaddress =IPAddress.Parse("10.128.230.79");

            //将ip地址和我们指定的端口号进行绑定

            IPEndPoint ipendpoint = newIPEndPoint(ipaddress, 88);

            //将socket和端口号进行绑定

            mysocket.Bind(ipendpoint);

            mysocket.Listen(0);

           mysocket.BeginAccept(AcceptCallback, mysocket);//异步接受多个客户端的连接

           

         

       }

       staticvoidAcceptCallback(IAsyncResult ar)

       {

         

            Socket mysocket = ar.AsyncState as Socket;

            //这里表示完成一次客户端的连接

            Socket clientSocket=mysocket.EndAccept(ar);

            //向客户端发送一条信息

            string msg = "hello客户端";

            byte[] data = System.Text.Encoding.UTF8.GetBytes(msg);

            clientSocket.Send(data);

           

           

           //我们异步从客户端那里接受多条数据

           clientSocket.BeginReceive(databuffer, 0, 1024, SocketFlags.None,AsyncCallback, clientSocket);

            //这里就是继续等待下一个客户端的连接

           mysocket.BeginAccept(AcceptCallback, mysocket);

 

 

 

        }

7.处理客户端关闭的异常

这里我们使用try catch捕捉异常就可以处理客户端远程强制退出的问题,当我们正常关闭的时候,客户端会向服务器发送长度为0的数据,所以我们这里判断一下如果为0,就关闭与客户端的连接

try

            {

               clientSocket = ar.AsyncState as Socket;

                int count = clientSocket.EndReceive(ar);

                if (count == 0)

                {

                    clientSocket.Close();

                }

            string msg = Encoding.UTF8.GetString(databuffer, 0, count);

            Console.WriteLine("从客户端接受信息" + msg);

           clientSocket.BeginReceive(databuffer, 0, 1024, SocketFlags.None,AsyncCallback, clientSocket);

             }

            catch(Exception e)

            {

                Console.WriteLine(e);

                if (clientSocket != null)

               {

                    clientSocket.Close();

                }

            }

这里我们也可以让客户端正常关闭

当输出c,就断开连接

while (true)

            {

                string msg2 = Console.ReadLine();

               clientSocket.Send(Encoding.UTF8.GetBytes(msg2));

               if (msg2 == "c")

                {

                    clientSocket.Close();

                    return;

                }

            }

8.tcp中的粘包和分包

这个粘包和分包是tcp的优化性能

粘包是如果信息过短就会将多次信息打一个包发送,如果信息过长则会分成多个包进行发送

在游戏开发中,我们常会产生多次小信息的传输,这里来讲解解决方案

我们可以在数值传输前加一个数据长度作为判断,假设你定义为40个字节,如果长度小于40个字节,就继续接受,如果到达40个字节,就进行传输

这里当我们要传递一个值类型的数据,就可以用BitConverter.GetBytes这种方式,将它转变一个四个字节的字节数组,这样就可以避免粘包的问题

下面就是一个解决的例子,我们用这个方法对字符串进行转换即可,我们把它做成一个工具类可以随时进行调用

//在这里专门创建一个方法,用来计算传递的字符串的长度,并且将它组拼成一个新的数组用来避免粘包的产生

       publicstaticbyte[] GetBytes(string data)

       {

            //将字符串转变为数组

            byte[] databuf = Encoding.UTF8.GetBytes(data);

            //计算字节数组长度

            int datalength = databuf.Length;

            //将它转变为4个字节长度的字节数组用来计算长度

            byte[]countLength= BitConverter.GetBytes(datalength);

            //组拼成新的字节数组,即长度加原本的数组

            byte[] newByte = countLength.Concat(databuf).ToArray();

            return newByte;

        }

9.关于服务器如何去接受客户端分包的信息

首先,我们让客户端向服务器循环发送100条输出,这种数据如果不进行解析就会出现粘包的情况

for (int i = 0; i < 100;i++)

            {

                //循环向客户端输出100条数据

               clientSocket.Send(Message.GetBytes(i.ToString()));

            }

然后,我们创建一个Message类来处理各种客户端传递过来的信息,首先,定义一个数组来接受客户端的信息,然后一个startIndex来记录当前传递过来的数据在数组中的位置,RemainLength记录剩余还能使用的长度,addCount用来更新当前数组存放到什么位置

  这里讲解一下我们的解析方法,当更新完StartIndex后,我们就要判断它的长度,首先用BitConvert.toInt32来获取数组的长度,如果超过了开始我们定义的数据长度的话,那就是我们现在解析完全的数据,我们用Encoding.Utf8来将字节数组进行转换,然后用Array.copy()来更新我们的数组,也就是将解析完全的数据移除数组,然后再更新我们的StartIndex

classMessage

   {   

       //每条信息存储的数组

       byte[] data= newbyte[1024];

       //这个是记录当前数组存储到第几个字节的标志位

       intstartIndex = 0;

       publicbyte[] Data

       {

            get { return data; }

       }

       publicint StartIndex

       {

            get { return startIndex; }

       }

       publicint RemainLength

       {

            get { return data.Length - startIndex; }

       }

       publicvoid AddCount(int count)

       {

            startIndex += count;

       }

       publicvoid ReadMessage()

       {

 

            while (true)

            {

                if (startIndex <= 4)

                {

                    return;

                }

                //读取数组的长度,自动省略前四个字节长度

                int count = BitConverter.ToInt32(data, 0);

                if (startIndex - 4 >= count)

                {

                    String s =Encoding.UTF8.GetString(data, 4, count);

                    Console.WriteLine("解析了一条数据:" + s);

                    Array.Copy(data, count + 4,data, 0, startIndex - count - 4);

                    startIndex -= (count + 4);

                }

                else { break; }

 

            }

       }

}

然后,我们在服务器中调用这个Message来的方法,首先我们接收到客户端的信息,存放到定义的数组里面,从开始索引开始存储,然后可存储的长度和数组剩余长度有关,如果超出的话就不会再进行接受

每次当我们接受到数据的时候,我们就获取数据长度,然后更新StartIndex,然后调用Message的解析方法,这样就可以循环解析数据并且避免了粘包的问题

lientSocket.BeginReceive(msg3.Data,msg3.StartIndex, msg3.RemainLength, SocketFlags.None, AsyncCallback,clientSocket);

            //这里就是继续等待下一个客户端的连接

           mysocket.BeginAccept(AcceptCallback, mysocket);

 

 

 

       }

       staticvoidAsyncCallback(IAsyncResult ar) {

 

            Socket clientSocket = null;

            try

           {

               clientSocket = ar.AsyncState as Socket;

                int count = clientSocket.EndReceive(ar);

                //解析接收到的数据

              

                msg3.AddCount(count);

                msg3.ReadMessage();

                if (count == 0)

                {

                    clientSocket.Close();

                }

          

               clientSocket.BeginReceive(msg3.Data, msg3.StartIndex, msg3.RemainLength,SocketFlags.None, AsyncCallback, clientSocket);

10.使用mysql做查询操作

   因为我们的丛林战争要使用mysql做数据库,这里来讲解一下mysql用法,首先我们要用msql创建一个数据库,定义好它的名字,连接ip地址,端口号,连接的用户名和密码,接着就是打开连接,创建命令语句,执行命令,然后按照列名读取出数据,具体操作可以参照代码和注释啦

                classProgram

   {

       staticvoid Main(string[] args)

       {

            string con = "Database=test_007;DataSource=localhost;port=3306;User Id=root;Password=1234;";

            //创建数据库的连接

            MySqlConnection coon = newMySqlConnection(con);

            //开启和数据库的连接

            coon.Open();

            //书写数据库操作的命令和指定的连接

            MySqlCommand command = new MySqlCommand("select * from user",coon);

            //执行查询操作并且将信息传递给reader

            MySqlDataReader reader =command.ExecuteReader();

            //这个方法会查询一行数据然后判断下一行是否还有数据

            while (reader.Read())

           {

                //根据列名获得数据

                string username = reader.GetString("username");

                string password = reader.GetString("password");

                Console.WriteLine(username + ":  " + password);

 

 

            }

            //执行结束关闭连接

            reader.Close();

            coon.Close();

            Console.ReadKey();

       }

   }

11.用Mysql做插入操作

这里的话我们使用的是字符串组拼进行的数据库操作,但是这样的操作一个问题,可能会因为用户的恶意操作对数据库进行污染,类似于密码为deletefromuser这种

stringusername = "haha";string password = "111";

            MySqlCommand cmd = new MySqlCommand("insert into user set username='" + username + "'" + ",password='" + password + "'",coon);

            //进行不查询的sql语句

            cmd.ExecuteNonQuery();

解决这个问题的方法就是用@进行注册变量,然后用AddWithValue来进行组拼

stringusername = "haha";string password = "111111";

            MySqlCommand cmd = new MySqlCommand("insert into user setusername=@un,password=@pwd",coon);

            cmd.Parameters.AddWithValue("un", username);

            cmd.Parameters.AddWithValue("pwd", password);

删除和更新的话就是update和delete,操作和上面类似,就介绍到这里了

12.服务器的分层架构

13.初步创建我们的服务器端

这里我们创建出服务器的Socket,提供接受端口号和ip地址创建IpendPoint的方法,并且使用异步的方法接受客户端传递的值

namespace游戏服务器.Sever

{

   classSever

   {

       //这里的话,我们创建我们游戏的服务器端

       privateIPEndPoint ipEndpoint;

      private  Socket severSocket;

       public Sever()

       {

 

       }

       //根据我们提供的Ip地址和端口号创立连接

       public Sever(string ipStr,int port)

       {

            SetIpAndPort(ipStr, port);

       }

       publicvoid SetIpAndPort(string ipStr,int port)

       {

            ipEndpoint = newIPEndPoint(IPAddress.Parse(ipStr), port);

       }

       publicvoid Start()

       {

            severSocket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            severSocket.Bind(ipEndpoint);

//表示最大连接长度为无限

            severSocket.Listen(0);

//接受客户端的连接

           severSocket.BeginAccept(AcceptCallBack, null);

 

       }

//处理连接回调

       publicvoidAcceptCallBack(IAsyncResult ar)

       {

            Socket clientSocket =severSocket.EndAccept(ar);

       }

 

}

14.创建我们的客户端

这里的话是处理我们和服务器端的一些交互和客户端要处理的事物,首先,我们要在客户端里创建自身的socket,并且持有到Sever端的Socket,然后我们在Start调用BeginReceive开始异步接受服务器端传递的信息,这里对于粘包分包的问题我们放在其他类里进行处理,然后我们写回调函数来处理接受的,当接收到的信息为0的时候,我们就断开和服务器之间的连接,这里我们用tryCatch来捕捉异常。然后我们书写一个Close方法,来关闭连接,这里,在Sever端我们提供一个方法来移除客户端,当然要先lock再移除,以防止出错

using System;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingSystem.Threading.Tasks;

usingSystem.Net.Sockets;

usingSystem.Net;

 

namespace游戏服务器.Sever

{

   classClient

   {

       private SocketclientSocket;

       private Seversever;

 

       publicClient()

       {

 

       }

       publicClient(Socket clientSocket, Sever sever)

       {

            //持有一个对socket的引用

            clientSocket = this.clientSocket;

            this.sever = sever;

       }

       //用来处理和服务器端之间的通信

       publicvoid Start()

       {

            clientSocket.BeginReceive(null, 0, 0,SocketFlags.None, ReceiveCallBack, null);

 

       }

       publicvoidReceiveCallBack(IAsyncResult ar)

       {

            try

            {

                int count = clientSocket.EndReceive(ar);

                if (count == 0)

                {

                    Close();

                }

           }

 

            catch (Exception e)

            {

                Close();

                Console.WriteLine(e);

            }

 

       }

       publicvoid Close()

       {

            if (clientSocket != null)

            {

                clientSocket.Close();

                sever.RemoverClient(this);

            }

       }

   }

}

15.引入Messgae类来对信息进行处理

using System;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingSystem.Threading.Tasks;

 

namespace游戏服务器.Sever

{

   classMessage

   {

       //在这里专门创建一个方法,用来计算传递的字符串的长度,并且将它组拼成一个新的数组用来避免粘包的产生

       publicstaticbyte[] GetBytes(string data)

       {

            //将字符串转变为数组

            byte[] databuf = Encoding.UTF8.GetBytes(data);

            //计算字节数组长度

            int datalength = databuf.Length;

            //将它转变为4个字节长度的字节数组用来计算长度

            byte[] countLength = BitConverter.GetBytes(datalength);

            //组拼成新的字节数组,即长度加原本的数组

            byte[] newByte = countLength.Concat(databuf).ToArray();

            return newByte;

       }

 

 

 

   }

}

16.完成Controller层的搭建

  首先,我们客户端向服务器发出的请求都是通过Controller层来进行处理的,这里来讲解一下如何初步搭建Controller层

   首先,我们要创建一个抽象基类BaseController,我们的子类都是继承自这个Controller,不同的需求对应不同的Controller

然后,我们创建一个Common的类库,这个是服务器端和客户端共享的代码,因为客户端向服务器端发出请求的时候,是通过RequestCode发送消息,而服务器端也需要这个RequestCode知道你调用的是哪一个Controller

这里我们创建一个枚举类型的RequestCode和ActionCode类

然后我们在BaseController里创建一个空的RequestCode和一个默认的实现方法

abstractclassBaseController

   {

       RequestCode requestCode = RequestCode.None;

 

       //子类可以选择性实现这个方法,这个是默认的处理方法

        publicvirtualvoid DefaultHandle()

       {

 

       }

    }

17.

这里来解释一下客户端和服务器端交互的原理

首先,当我们的客户端向服务器端发起了一个请求,比如注册,这时候我们会向服务器的Controller层发送一个数据长度+RequestCode+ActionCode+数据的这样一个信息,数据长度用来解决粘包分包的问题,RequestCode用来判断用哪一个Controller来执行,ActionCode用来判断用哪一个方法来解决这个请求,这两个Code都是int类型的四个字节的数据

然后我们的Controller层也会根据你的请求进行判断,如果是要做出响应的请求,服务器端就会向客户端回发一个数据长度+RequestCode+数据的信息,我们的客户端会根据对应的RequestCode进行相应的反应,比如注册请求的响应,就会回复一个注册成功

这里的话我们一个服务器端可以同时响应多个客户端的请求,而一个客户端只能回应一个服务器的RequestCode

18.创建我们的ControllerMannager

这里的话,我们需要一个类来管理我们的Controller,并且这个管理类为了避免冗余度过高,我们选择用Sever服务器端作为中介者,来进行交互

首先我们要一个字典,用来存储各种Controller,然后我们就要对它进行初始化了

namespace游戏服务器.Controller

{

   classControllerManager

   {

       //这个是用来管理Controller,来决定什么时候调用什么Controller

       //首先创建一个字典,用来存储各种Controller

       privateDictionary<RequestCode,BaseController> requestDictionary=newDictionary<RequestCode,BaseController>();

       publicControllerManager()

       {

            Init();

       }

       void Init()

       {

            //在这里进行初始化

       }

   }

}

这里我们在Sever构建一个接口,以后其他类想要使用ControllerMannager,都要通过Sever类来执行

private ControllerManagercontrollerManager = newControllerManager();

19.通过ControllerMannager来处理分发的请求

首先,我们要提供一个DefaultController来判断当客户端传递的是没有ActionCode的时候来怎么处理,当然这里首先我们要在BaseController提供一个方法来获得它的RequestCode

publicRequestCode RequestCode

       {

            get

            {

                return RequestCode;

            }

        }

这样我们就可以在ControllerMannager里实例化DefaultController来进行错误处理了

void Init()

       {

            //给一个处理错误的Controller

            DefaultController defaultController= newDefaultController();

           requestDictionary.Add(defaultController.RequestCode, defaultController);

        }

然后的话,我们就提供一个方法来处理客户端向服务器端请求Controller的方法,首先,我们要传递过来客户端发送的数据,请求的RequestCode和ActionCode,然后我们获得基类的Controller,根据tryGetValue来判断是否存在客户端请求的requestCode是否有对应的Controller,如果有的话,我们就要用Enum里的GetName,根据类型,来获得ActionCode里的值,再将其转变成为字符串类型,这里我们ActionCode里面定义的是None这个值类型,我们将其转换成字符串类型

接下来的话,我们运用的就是C#里的反射机制,通过controller.getType.getMethod,来根据方法名获取到方法信息,这里的话,我们需要引用一下System.Reflection

如果存在这个方法的话,我们就可以用MethodInfo里的invoke方法,来根据参数调用这个方法了,这里参数需要是object[]类型,我们转换一下就可以使用了前面的参数是指在什么下运行,我们指定为controller,这样的话,我们就可以调用方法了,当然这里有个返回值,判断方法调用是否需要给客户端做出回应,这个我们后面再讲

 

publicvoidHandleRequest(RequestCode requestCode,ActionCode actionCode,string data)

       {

            BaseController controller;

            //判断是否根据对应的RequestCode获取到对应的Controller

            bool isGet=requestDictionary.TryGetValue(requestCode, out controller);

            if (isGet == false)

            {

                Console.WriteLine("无法获得到" + requestCode + "对应的Controller");return;

            }

            //如果有对应的RequestCode的话,我们就要获取到对应的方法名,这里是获取到对应ActionCode里的值

            string methodName = Enum.GetName(typeof(ActionCode), actionCode);

            //这里是用到C#中的反射机制,将对应类型的对应的方法名的方法信息获得到

          MethodInfo mi= controller.GetType().GetMethod(methodName);

            if (mi == null)

            {

                Console.WriteLine("该方法" + methodName + "不存在");

                return;

            }

            //在controller中调用

            object[] paramaters = newobject[] { data };

            //根据o来判断是否响应

            object o= mi.Invoke(controller, paramaters);

 

       }

       

    }

19.处理客户端请求的响应

首先,我们BaseController默认的方法,传递的除了数据以外,还有对应的客户端和服务器端

  publicvirtualstring DefaultHandle(string data,Clientclient,Sever sever)

       {

            //默认不给客户端返回数据

            returnnull;

        }

然后的话,我们在ControllerMannager中传递对应的服务器

  private Sever sever;

       publicControllerManager(Sever sever)

       {

            this.sever = sever;

            InitController();

        }

这样的话,在Sever端的构造函数里,我们就可以直接构造出ControllerManager并且将自身传递过去

  public Sever(string ipStr,int port)

       {

            controllerManager = new ControllerManager(this);

            SetIpAndPort(ipStr, port);

        }

然后我们怎么处理对客户端发送消息的响应呢,我们在Sever端里提供一个处理方法,SendMessage,用来作为客户端和ControllerManager的中介,通过这个方法来处理课都俺的信息

publicvoid SendMessage(Clientclient,RequestCode requestCode,string data)

       {

            //TODO

        }

这样我们就可以在ControllerManager里调用sever的这个方法来处理客户端的请求了

     object o=mi.Invoke(controller, paramaters);

            if (o == null || string.IsNullOrEmpty(o.ToString()))

            {

                //如果空就不进行处理

                return;

            }

            //通过sever端进行中介来发送数据

           sever.SendMessage(client, requestCode, o asstring);

猜你喜欢

转载自blog.csdn.net/qq_36574586/article/details/78759036
今日推荐