• RemObjects 客户端完整执行流程


    RemObjects是一个强大的N层网络框架,写一个实现即可通过多种连接方式(http,tcp/ip,webservice...)被不同客户端调用
    这里我将通过源代码展示一下客户端的调用过程,因为RO封装得实在太好,程序员根本不需要关心太多细节,只需要根据需求编写实现即可,了解其实现原理,可增强自己的设计思想,现在请一步一步来看下这个过程
    图片
    首先是客户端调用就是这么简单,一个TRORemoteService对象转成服务端的一个接口,即可调用服务端的接口函数,
    其实在程序启动的时候就已经完成了大多数工作了,先是看下主窗体的文件引用
    图片 
    当在界面摆上以下3个控件


     ROMessage: TROBinMessage; 
     RORemoteService: TRORemoteService;
     ROChannel: TROIndyTCPChannel;
    会自动引入上图的文件, 这里的核心文件是 uROClient
    先看下其初始化代码
    图片 
    该代码主要是创建了针对 消息类,通
    道类,代理类 的管理列表对象 

    _MessageClasses := TClassList.Create; 消息类列表
    _ProxyClasses := TStringList.Create; 代理类列表
    _TransportChannels := TStringList.Create; 通道类列表

    图片
    当我们使用一种消息格式的类时, 这里以BIN格式为例,引用的uROBinMessage文件会在程序启动之初,
    将该对象处理类TROBinMessage加入消息类的列表
     图片
    通道类也一样,这里以TROIndyTCPChannel 为例 

    图片

    图片

    客户端要调用服务端的接口INewService,需要服务端提供一个接口代理类TNewService_Proxy,
    初始化时也是通过注册函数添加到uROClient里的代理类对象,
    方式和 消息类,通道类 的过程类似 
    图片 
    图片

    以上部分就是程序一启动时所做的初始化工作

    =============================

    回到客户端的代码,一句代码是如何实现整个调用过程的呢?
    图片
    首先RORemoteService是一个接口实现类对象,调用 as 操作时会使用接口查询,看下代码
    图片 
    这里会调用 FindProxyClass 函数到_ProxyClasses 代理类列表对象里搜索我们要调用的服务端接口 INewService 的代理类,
    如果找到则返回该代理类对象,因为程序启动时 TNewService_Proxy 已经注册到 _ProxyClassess 里,所以这里实际返回的就是
    TNewService_Proxy,返回后再创建实例对象proxy := proxyclass.Create(Self); 
     构造函数的参数是 TRORemoteService 对象自身;(RORemoteService as INewService) 返回的就是代理类的对象,
    那么在创建代理类对象又做了些什么呢?
    图片 TNewService_Proxy 类没有构造函数,所以这里调用的是其父类TROProxy 的构造函数
    图片
    图片
    因为 RORemoteService 的对象的 Channel,Message属性分别为 ROChannel,ROMessage;
    所以代理类对象其实就是把这两个对象给保存起来

    返回来代理类对象,接下来是调用接口函数 Sum
    图片
    代理类的__GetMessage属性获取的就是构造函数里传入的TRORemoteService对象的 Message 属性
     图片
    同理获取通道对象,所以这里的 lMessage 实际上就是窗体的  ROMessage: TROBinMessage; 对象
    lTransportChannel 实际上就是窗体的 ROChannel: TROIndyTCPChannel; 对象

    接下来的代码开始对入参进行指定消息格式的打包

    lMessage.InitializeRequestMessage(lTransportChannel, 'NewLibrary', __InterfaceName, 'Sum');
    lMessage.Write('A', System.TypeInfo(Integer), A, []);
    lMessage.Write('B', System.TypeInfo(Integer), B, []);
    lMessage.Finalize;
    首先是在请求数据头里写入要调用服务端接口及函数名称,然后再以序列化的方式写入入参数据图片
    图片
    图片
    图片
    图片
    图片

    这里用到的对象序列化思想,简单地说就是把一个对象按一定的格式保存到流对象里,这个过程可以理解为 封包
    数据按消息类打包后,调用通道类进行传输
    lTransportChannel.Dispatch(lMessage);
    图片

    这里的核心函数 IntDispatch 处理了 
    连接:IndyClient.Connect;
    发送:IndyClient.IOHandler.Write(lStream, lStream.Size, TRUE);
    接收:IndyClient.IOHandler.ReadStream(lStream);
    断开:IndyClient.Disconnect;
    这里的 IndyClient 是 TIdTCPClient 类对象,
    也就是 通道类TROIndyTCPChannel 底层通讯其实就是通过TIdTCPClient 实现的
     
    图片 
    图片 
    服务端接收到数据后会 解包,然后调用数据头里的接口函数,计算返回的数据再 打包  发送到客户端,
    客户端接收到返回的数据后,再 解包,并读取返回结果
    lMessage.Read('Result', System.TypeInfo(Integer), Result, []);

    以上这就是整个客户端的调用过程
  • 相关阅读:
    关于添加类目
    关于协议传值
    Block传值
    UIImagePickerController(相册的使用)
    Handler(单例传值)
    UISearchController
    org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.microsoft.sqlserver.jdbc.SQLServerDriver '
    vs2015 打开项目自动运行 npm install
    Apktool下载与安装 windows环境
    React-Native 问题随记2: com.android.builder.testing.api.DeviceException
  • 原文地址:https://www.cnblogs.com/erp-system/p/5639266.html
Copyright © 2020-2023  润新知