• 使用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;
            }
    
        }
    }

    运行效果

     

  • 相关阅读:
    org.apache.commons.net.ftp
    java中的匿名内部类总结
    有关JVM处理Java数组方法的思考
    使用myeclipse创建带注解的model实体类
    Maven导出Project依赖的jar包
    annotation-config, annotation-driven, compont-scan 区别
    hibernate annotation注解方式来处理映射关系
    关于Spring事务<tx:annotation-driven/>的理解(Controller可以使用@Transactional)
    Hibernate批量操作(二)
    Hibernate批量操作(一)
  • 原文地址:https://www.cnblogs.com/kewei/p/3020125.html
Copyright © 2020-2023  润新知