循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法

    (本文是专门针对未接触过C/S开发的初学者而写的,C/S开发高手请自动忽略啊~~)

   还在写“Hello world!”式的单机程序吗?还在各种拖控件吗?是否自己都觉得有点low呢?来个质的飞跃吧!看看怎么让服务器帮咱做加法!

     所谓C/S程序就是Client/Server程序,自然既包含一个Client项目,又包含一个Server项目。所以先来新建解决方案及项目。

一·开端

1.新建解决方案及项目 (针对初学者,已掌握者跳过)

    1.新建一个Windows窗体项目,项目命名为Server。

    2.右击右击解决方案,添加新建项目,命名为Client. 

    现在,传说中的C/S架构就已经初现端倪了。

    

       既然是C/S程序,必然涉及Client和Server之间的通信,这也是C/S程序和单机程序最大的区别。同时也是初学者进步路上的拦路虎。问题的关键正是在于如何实现Client和Server之间的通信。可是今天我们不必纠结于任何关于网络通信技术的琐碎与繁难,却能够妥善,简约,完美的将问题解决,而这就需要我们站在巨人的肩膀上。所谓“君子性非异也,善假于物也。”,这里我们要借助于稳定强大的ESFramework通信框架,利用它来实现我们将加法交给服务器来做的小小心愿。(怎么感觉有点杀鸡用牛刀呢?)    

2·添加引用(针对初学者,已掌握者跳过)

      右击项目,添加引用。

      Client和Server两个项目都要引用哦。

      

二·核心实现 

      好了,现在宝贝已经搞到手了。可是宝贝再好,不会用也是白搭。ESFramework究竟怎么用呢?更具体一点,究竟如何用它来帮助我们实现把加法交给服务器来做的需求呢?这就需要用到 ESFramework提供给我们的两件利器RapidServerEngine(服务端通信引擎)和 RapidPassiveEngine(客户端通信引擎)。其实说到底,天下只有一件利器,那就是Engine(通信引擎),它分为服务端和客户端两部,配合使用方能鸾凤和鸣!虽然引擎二字听起来充满了工业时代的气息,但实际上这台引擎并不像我们想象的那样笨重、机械,相反,它更像当今时代我们人手一部的手机,小巧轻便却功能超强!随时随地通信无阻! 有了这两部引擎,服务端和客户端便能畅爽的通信!  

    既然通信的核心部件就是这两部引擎,那么我们不妨先将他们装配到我们的项目中再说!

1·装配通信引擎

(1)服务端装配通信引擎

namespace Server
{
    static class Program
    {       
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //创建服务端引擎 
            IRapidServerEngine serverEngine = RapidEngineFactory.CreateServerEngine();         
            Application.Run(new Form1());
        }
    }
}

       之所以在Main()方法中来创建serverEngine,是因为serverEngine是承担通信重任的核心部件,所以在程序运行之初就要准备好。(当然,Main方法是应用程序的主入口点这个大家都知道。)要注意的一点是:一定要在 Application.Run(new Form1());语句之前添加代码哦,因为窗体跑起来之后,后面的代码就不会被执行了,大家可以自己动手试一下。另外,大家自己在照着添加上述代码的时候,会出现红色下划线的情况,不要紧张,光标放上去,然后会有蓝色小短杠出现,光标再移上去,添加Using即可。

    

(2)客户端装配通信引擎

namespace Client
{
    static class Program
    {
        //创建客户端通信引擎,公有静态方便外部调用
        public static IRapidPassiveEngine clientEngine = ESPlus.Rapid.RapidEngineFactory.CreatePassiveEngine();
        
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);          
            Application.Run(new Form1());
        }
    }
}

2·实现消息处理器

      接下来我们要做第二个重要部署,同时,ESFramework为我们提供的第二个法宝也要粉墨登场了!那就是CustomizeHandler(自定义消息处理器)。收发消息的重任已经由Engine(通信引擎)一力承担了,而处理消息的重任就交给CustomizeHandler(自定义消息处理器)了!

      既然是自定义消息处理器,那么肯定是留给程序员自己来添加相应的处理逻辑的,ESFramework通信框架为此预留了ICustomizeHandler接口,所以我们自己添加一个类来实现ICustomizeHandler接口。

(1)服务端添加ServerHandler类 

namespace Server
{
    class ServerHandler : ICustomizeHandler//实现接口
    {
        public void HandleInformation(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();
        }

        public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();
        }
    }
}

(2)客户端添加ClientHandler类(与服务端类似,略)  

(3)服务端装配消息处理器

namespace Server
{
    static class Program
    {       
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //创建服务端引擎     
            IRapidServerEngine serverEngine = RapidEngineFactory.CreateServerEngine();
            //创建服务端消息处理器           
            ServerHandler serverHandler = new ServerHandler();
            Application.Run(new Form1());
        }
    }
}

(4)客户端装配消息处理器(与服务端类似,略)

3·通信引擎初始化

     接下来我们要做一件重要的工作了:通信引擎初始化!

(1)服务端引擎初始化

       对于serverEngine(服务端通信引擎),我们需要设置好用于通信的端口号,并且将之前创建好的serverHandler(服务端消息处理器)装配到serverEngine中。这是因为,收到消息是处理消息的前提,当Engine收到消息后就需要对收到的消息进行相关处理,而这正是Handler的职责,所以Engine要将Handler包含其中才能够方便的把收到的消息就地进行处理。

namespace Server
{
    static class Program
    {       
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //创建服务端引擎   
            IRapidServerEngine serverEngine = RapidEngineFactory.CreateServerEngine();  
            //装配服务端消息处理器          
            ServerHandler serverHandler = new ServerHandler();
            //设置好服务器用来通信的端口号,并将消息处理器传入引擎
            serverEngine.Initialize(2000, serverHandler);
            Application.Run(new Form1());
        }

 (2)客户端引擎初始化

      对于clientEngine(客户端通信引擎)而言则需要设置好用户ID,登陆密码,欲要连接的服务器IP及端口号,同时也要装配好clientHandler。

namespace Client
{
    static class Program
    {
        //创建通信引擎,公有静态方便外部调用
        public static IRapidPassiveEngine clientEngine = ESPlus.Rapid.RapidEngineFactory.CreatePassiveEngine();
        
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //创建客户端消息处理器
            ClientHandler clientHandler = new ClientHandler();
            //初始化客户端引擎,该操作将与目标服务器建立TCP连接,方法返回值为登陆的结果状态
            LogonResponse logonResponse = clientEngine.Initialize("1", "123", "127.0.0.1", 2000, clientHandler);
            Application.Run(new Form1());
        }
    }
}

 三·业务逻辑编写

       上述过程其实都是准备工作,主要就是引用ESFramework通信框架并将框架提供的重要“设备”(通信引擎和消息处理器)准备到位,这样一来,之前探讨得出的核心问题,即如何实现客户端与服务端之间的通信,就已经不成问题了,现在我们就可以考虑利用ESFramework提供的相关API来编写我们的业务逻辑了!

  1.客户端向服务端发送消息 

     客户端向服务器发送消息的核心就是利用框架提供的clientEngine.CustomizeOutter.Send(int informationType, byte[] info)方法,来把待相加的两个整数打包发送给服务器,并附加消息类型为做加法,这样服务器在收到消息后就可以将消息解析出来并进行相应的处理了。

        private void button1_Click(object sender, EventArgs e)
        {
            int leftNum = int.Parse(this.textBox_leftNum.Text);
            int rightNum = int.Parse(this.textBox_rightNum.Text);
            //将数据转码以备发送给服务端
            byte[] leftNumCode = BitConverter.GetBytes(leftNum);
            byte[] rightNumCode = BitConverter.GetBytes(rightNum);
            byte[] NumbersCode = new byte[8];//用来合并待发送的消息(一个整型占4个字节)
            for (int i = 0; i < 4; i++)
            {
                NumbersCode[i] = leftNumCode[i];//第一个数的编码装进前四位
            }
            for (int j = 0; j < 4; j++)
            {
                NumbersCode[j + 4] = rightNumCode[j];//第二个数的编码装进后四位
            }
           //发送给服务器,100代表做加法这件事
            Program.clientEngine.CustomizeOutter.Send(100, NumbersCode);
        }

  2.服务端接收并处理消息,并将处理结果发送给客户端

      服务端的通信引擎serverEngine将自动接收到客户端发送过来的消息,并且将接收到的消息递交给消息处理器serverHandler来处理,这也是之前为什么要把serverHandler传递进来的原因。现在我们要做的就是去ServerHandler类中来编写相应的处理逻辑。但是要注意一点,那就是ServerHandler在处理完消息后需要将处理结果再发送给客户端,而收发消息是通信引擎的职责,所以我们需要将serverEngine作为参数传进来。而发送消息仍然要仰仗框架提供的serverEngine.CustomizeController.Send(string userID, int informationType, byte[] info)方法。

    class ServerHandler : ICustomizeHandler//实现接口
    {
        private IRapidServerEngine serverEngine;

        public ServerHandler(IRapidServerEngine _serverEngine)
        {
            this.serverEngine = _serverEngine; 
        }
        //该方法用来处理客户端通过clientEngine.CustomizeOutter.Send()方法发送过来的消息
        public void HandleInformation(string sourceUserID, int informationType, byte[] info)
        {
            if (informationType == 100)
            {
                int leftNum = BitConverter.ToInt32(info, 0);//将字节数组前四位还原成leftNum
                int rigthNum = BitConverter.ToInt32(info, 4);//将字节数组后四位还原成rigthNum
                int result = leftNum + rigthNum;
                byte[] resultCode = BitConverter.GetBytes(result);//将计算结果转码
                this.serverEngine.CustomizeController.Send(sourceUserID, 100, resultCode);//发送给对应的客户端
            }          
        }
        //该方法用来处理客户端发送过来的请求,那是另一种通信方式,这里姑且不论
        public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();//接口的默认实现
        }
    }

  3.客户端接收并分析消息,并将结果显示到界面

    客户端通信引擎clientEngine收到消息后仍然需要交付给消息处理器ClientHandler来处理。

    class ClientHandler : ICustomizeHandler
    {
        //该方法来处理服务端Send()方法发送过来的消息
        public void HandleInformation(string sourceUserID, int informationType, byte[] info)
        {
            if (informationType == 100)
            {
                MessageBox.Show("结果为:" + BitConverter.ToInt32(info, 0));
            }
        }

        public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();
        }
    }

 四·编译运行

  1.先启动服务端,右击Server项目,调试,启动新实例

  2.启动客户端

     大功告成!

    源码下载:CSAdd.zip

五·总结

      虽然本文篇幅较长但是整个逻辑其实非常简单!之所以篇幅较长主要是因为花了较多的笔墨照顾初学者,同时也是因为尽力在营造循序渐进的逻辑过程,为了让每一步水到渠成,不突兀,不跳跃,惟其如此才是符合认识论的学习过程,同时也是我们实际编程中应该掌握的思维方式。     

      回头来看,其实所有的工作只分为两步:

      1.引用通信框架并做好相关的准备工作,使得“如何实现通信”这一重要艰巨的问题刹那间迎刃而解!

      2.编写相关的业务逻辑。

      而1中的工作是超简单的,一回生二回熟,以后无论编任何C/S程序都是这个套路,从此我们只需要专注于具体的业务逻辑而不用理会关于底层通信技术的一切琐碎与繁难了!而这将大大提高工作效率!

      援引*同志的一句名言做结:“科学技术是第一生产力!”,也献给所有致力于成为IT精英的朋友!

      

   

原文地址:https://www.cnblogs.com/aoyeyuyan/p/4347540.html