使用FastTcpServerEx构建服务器

前言

本小节是NetworkSocket系列的第6小节,在阅读本小节之前,您可能需要先阅读前面的几个小节,否则可能觉得内容跳转比较大。

描述

FastTcpServerEx是从TcpServerEx派生,使用的协议和TcpServerEx完成相同,FastTcpServerEx充分结合C#强大的反射功能,大大地简化了服务器编程难度,更符合实际通讯项目的编写,与之相比,前两章节提到的TcpServerBase和TcpServerEx构建服务器,离实际项目要求还相差很远。FastTcpServerEx的工作原理是,当收到客户端发来的数据DataEventExArgs后,分析DataEventExArgs的Action参数,把Action和对应的服务方法绑定,把DataEventExArgs.Binary转换为服务方法对应的参数,然后通过反射调用服务方法,将方法的返回值再封装为DataEventExArgs返回给客户端,这里虽然用到了反射,但已经改善和优化过,方便性的提升带来的价值大过于性能的损失价值。

服务器编写思路

你可以把FastTcpServerEx比作Wcf来理解,编写步骤是:1、编写服务契约IDemoServer,当然还有用到的实体,建议实体单独作一个项目工程,这样在序列化工作很方便;2、新增DemoServer类,派生于FastTcpServerEx,并继承IDemoServer接口;3、实现IDemoServer接口

编写接口

新建Server工程,引用NetworkSocket.dll,引入NetworkSocket、NetWorkSocket.TcpExtention、NetWorkSocket.Serialization、Entity命名空间,添加IDemoServer接口

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entity;
using NetWorkSocket;
using NetWorkSocket.TcpExtention;

namespace Server
{
    /// <summary>
    /// 服务接口
    /// 要求每个方法的第一个参数必须是SocketAsync(DataEventExArgs)类型
    /// 这个参数很重要(验证客端,断开客户端等都用到)
    /// </summary>
    public interface IDemoServer
    {
        /// <summary>
        /// 客户端登录
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="user">用户信息</param>
        /// <param name="fAdmin">是否是管理员登录</param>
        /// <returns></returns>
        bool Login(SocketAsync<DataEventExArgs> client, Userinfo user, bool fAdmin);

        /// <summary>
        /// 求和
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="x">参数x</param>
        /// <param name="y">参数y</param>
        /// <param name="z">参数z</param>
        /// <returns></returns>
        int GetSum(SocketAsync<DataEventExArgs> client, int x, int y, int z);

        /// <summary>
        /// 获取客户端本机上的时间
        /// 此方法由客户端来实现,服务器来调用
        /// [ClientMethod]是修饰此方法为客户端方法的特性
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="callBack">回调,收到数据后,将回调此方法</param>
        [ClientMethod]
        void GetDateTime(SocketAsync<DataEventExArgs> client, Action<DateTime> callBack);
    }
}

上面的接口有三个服务方法,分别为客户端登录、客户获取数据相加、获取客户端本机时间,前两个方法都是客户端主动请求,服务器被动处理,而后一个方法是服务器主动请求,客户端被动处理(即服务器和客户端功能倒置)。这里要注意的是,每个方法的第一个参数必须是SocketAsync<DataEventExArgs>,这个参数不是数据参数,不会被序列化然后传送到另一端,而是在方法的实现中会经常用到这个参数。

实现接口

实现接口也就是实现了服务器的功能,这里也就不多篇幅来说明怎么实现接口了。

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entity;
using NetWorkSocket;
using NetWorkSocket.Serialization;
using NetWorkSocket.TcpExtention;


namespace Server
{
    /// <summary>
    /// 服务器接口的实现
    /// 第一个接口必须是服务器的接口(如果有多个接口的话)
    /// </summary>
    public class DemoServer : FastTcpServerEx, IDemoServer
    {
        /// <summary>
        /// 重写验证客户端的方法
        /// 在客户端请求服务时,此方法第一时间被调用
        /// 如果返回flase,则服务器会丢弃客户端的请求
        /// </summary>
        /// <param name="client"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        protected override bool ValidateClient(SocketAsync<DataEventExArgs> client, int action)
        {
            // action为0,对应接口的第一个方法,也就是Login方法
            bool forLogin = (action == 0);

            // 如果此请求不是Login类型请求
            if (forLogin == false)
            {
                // 验证通过后才可以请求其它服务方法
                return client.HasValidated;
            }
            return base.ValidateClient(client, action);
        }

        /// <summary>
        /// 客户端登录
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="user">用户信息</param>
        /// <param name="fAdmin">是否是管理员登录</param>
        /// <returns></returns>
        public bool Login(SocketAsync<DataEventExArgs> client, Userinfo user, bool fAdmin)
        {
            if (user != null)
            {
                // 标记客户端为合法有效的用户
                // 这样,client在登录后,就可以有权调用GetSum服务方法
                client.HasValidated = true;
                client.UserToken = user; // 保存user对象,方便查找client,client可以从this.AliveClients属性查找

                // 从客户端获取时间
                this.GetDateTime(client, (time) =>
                {
                    Console.WriteLine("{0}({1}) {2}", user.name, fAdmin ? "Admin" : "User", time);
                });

                // 返回登录成功
                return true;
            }

            return false;
        }


        /// <summary>
        /// 求和
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="x">参数x</param>
        /// <param name="y">参数y</param>
        /// <param name="z">参数z</param>
        /// <returns></returns>
        public int GetSum(SocketAsync<DataEventExArgs> client, int x, int y, int z)
        {
            return x + y + z;
        }

        /// <summary>
        /// 获取客户端本机上的时间
        /// 此方法由客户端来实现,服务器来调用
        /// [ClientMethod]是修饰此方法为客户端方法的特性
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="callBack">回调,收到数据后,将回调此方法</param>
        [ClientMethod]
        public void GetDateTime(SocketAsync<DataEventExArgs> client, Action<DateTime> callBack)
        {
            // 使用InvokeClient简化发送数据到客户端的过程
            // 如果没有数据参数,InvokeClient的第二个参数可以为null
            base.InvokeClient<DateTime>(client, null, callBack);
        }

    }
}

当接口实现后,服务器的功能已编写完成,下面是启动服务器的方法

View Code
 static void Main(string[] args)
        {
            Console.Title = "Flex Server";
            DemoServer server = new DemoServer();
            server.StartListen(new IPEndPoint(IPAddress.Any, 8181));
            Console.WriteLine("127.0.0.1 8181");

            while (true)
            {
                Console.ReadLine();     
            }
        }

生成服务调用代理

wcf的时候,我们把服务发布后,通过vs很方便就可以生成服务调用的代理类,FastTcpServerEx也有类似功能,不同的是,这个代理类是通过ProxyMaker工具来反射IDemoServer,获取里面的方法,然后逆向生成调用代码,最终于编译为Server.dll输出,客户端只要引用Server.dll,就可以方便的和服务器通讯了。

运行ProxyMaker

这里对应我们的命令是:ProxyMaker.exe /a ..\Demo\Server\bin\Debug\Server.exe /i IDemoServer ,我们可以把它放到批处理文件,以后双击就可以编译出Server.dll;

编写客户端

有了Server.dll,编写客户端的难度也大大的降低了,新建Client工程,引用NetworkSocke.dll和刚才生成的Server.dll;实例化 DemoServer client = new DemoServer();然后就可以调用里面的方法了,这里和wcf客户端几乎完全一样;由于比较简单,客户端代码中我就不注释了。

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using Entity;
using Server;

namespace Client
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        static void Main()
        {
            DemoServer client = new DemoServer();

            client.OnGetDateTime += new DemoServer.GetDateTime(client_OnGetDateTime);

            client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8181));

            Random ran = new Random();
            Userinfo user = new Userinfo() { password = "123456" };

            while (true)
            {
                Console.ReadLine();
                user.name = "A" + ran.Next(0, 100).ToString();

                client.Login(user, true, (state) =>
                {
                    if (state)
                    {
                        int x = ran.Next(0, 100);
                        int y = ran.Next(0, 100);
                        int z = ran.Next(0, 100);

                        client.GetSum(x, y, z, (sum) =>
                        {
                            Console.WriteLine("{0} + {1} + {2} = {3}", x, y, z, sum);
                        });
                    }
                });
            }
        }

        static DateTime client_OnGetDateTime()
        {
            return DateTime.Now;
        }

    }
}

运行效果

 

原文地址:https://www.cnblogs.com/kewei/p/3020125.html