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

    }
}

運行效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM