笔者在本科和研究生的计算机专业课上,学习过 Unix 环境下的网络编程,其中就包括用 C 语言实现 TCP 客户端和服务器端。
这是我们当年上课使用的教材。
传输控制协议(Transmission Control Protocol,简称 TCP) 是一种面向连接的、可靠的传输层协议。它确保了数据在网络中可靠地传输,不会出现丢包、重复或者顺序错误的问题。
TCP 协议通过建立连接、数据传输、流量控制、拥塞控制等机制来保证通信的可靠性。它为应用层提供了一种可靠的字节流服务。
套接字(Socket) 是计算机网络中用于进程间通信的一种抽象概念。它为程序提供了一种网络通信的接口,使得应用程序能够通过它发送和接收数据。
套接字有多种类型,包括面向连接的套接字(如 TCP 套接字)和无连接的套接字(如 UDP 套接字)。
在 TCP/IP 详解这本书里,介绍了 TCP 客户端和服务器端建立连接时的三次握手概念。
ABAP Push Channel 框架提供的工具类,对 TCP Socket 的概念进行了高度封装。
ABAP 开发人员不需要了解 TCP 协议的底层技术细节。
使用 ABAP 进行 TCP 客户端的开发,只需要调用 ABAP Channel 框架提供的对应方法,实现建立连接,发送数据到服务器端,以及从服务器端接收数据这三大块需求就可以了。
ABAP Channel 是一种 ABAP 应用服务器与 Internet 通过 Event-Driven 即事件驱动方式进行交互的机制和技术。
ABAP Channel 分为 ABAP Messaging Channel 和 ABAP Push Channel(以下简称 APC) 两类。本文使用的是 ABAP Push Channel 提供的工具类。
SAP 系统已经自带了一个用 APC 实现的 ABAP TCP Client 的报表:DEMO_APC_TCP_CLIENT,总共一百多行的代码。
TCP 服务器端的搭建
既然有客户端,那么一定需要对应的服务器端。该报表使用了一个网络工具领域的瑞士军刀 NCAT,可以在其官网下载:
该下载到本地后是一个 .exe 文件,可以通过命令行指定不同的启动参数,扮演网络客户端和服务器端的不同角色。
如下图所示,报表首先使用 cl_gui_frontend_services 的 get_ip_address 方法,获取本地电脑的 IP 地址。然后在第 14 行,使用 cl_gui_frontend_services 的 execute 方法,调用操作系统的 cmd.exe 命令行工具,启动刚刚下载好的 NCAT 工具的 exe 文件。
-l 参数代表 NCAT 以服务器模式启动,-l 后面跟的参数值,内容为该服务器监听的 IP 地址。-p 参数指定服务器监听的端口号。
ABAP TCP 客户端的实现
使用 cl_apc_tcp_client_manager 的 create 方法,创建一个新的客户端实例。
该方法需要传递服务器端的 IP 地址和端口号,以及一个 TCP Frame 参数。Frame 的意思是帧,其主要任务是提供有关在即将创建的 TCP 连接上发送和接收的数据结构的信息。
接收数据时,APC 框架通过该结构的信息,能够将服务器返回的数据,拆分为将要传送给客户端的 TCP 包。当客户端发送数据到服务器端时,APC 将根据 create 方法指定的帧信息,检查客户端发送的数据格式是否有效。
本例使用了 IF_APC_TCP_FRAME_TYPES=>CO_FRAME_TYPE_TERMINATOR 作为帧类型,意思是由 ABAP 开发人员自行指定一个 TCP 帧的结束符号(Terminator).
上图第六行的 terminator 变量,值为硬编码的 0A,这是换行符 LF(Line Feed)的 Unicode 编码值。
既然 APC 是一种基于事件驱动的通信机制,必然少不了 Event Handler 的实现。
上图第 7 行代码,调用 create 创建 TCP 客户端实例时,还需要给其维护一个事件处理类的实例。
这个事件处理类的实现,语义也很清晰,如上图所示。
- 基于 ABAP Push Channel 机制实现的事件处理类,需要实现标准接口 if_apc_wsp_event_handler.
- 该接口声明的 on_open 方法,会在客户端和服务器端成功建立 TCP 连接后自动触发。这个方法内,可以通过输入参数 I_CONTEXT,获得更多的关于成功建立的 TCP 连接上下文信息。
- 该接口声明的 on_message 方法,在服务器端有数据推送到客户端时自动触发。上图通过 i_message 的 get_text 方法,将服务器端发送回来的字符串数据,存储到类实例的 message 成员变量里。
客户端实例创建成功之后,调用其 connect 方法,即可与服务器建立 TCP 连接。
客户端发送数据到服务器端
下面的代码都是自描述的,语义很容易理解。
- 调用 client 实例的 get_message_manager 方法,拿到客户端的消息管理器实例。
- 调用消息管理器的 create_message 方法,生成一条新的空白消息。
- 将硬编码的 TCP 帧终止符,即换行符的 Unicde Code 0A, 转换成二进制格式。
- 将用户硬编码的
Hello TCP, answer me!
字符串转换成二进制格式。将第三步换行符的二进制格式添加到其尾部,组装成一个完整的 TCP Frame. - 将上一步骤组装好的 TCP Frame,调用消息管理器的 send 方法发送给服务器。
客户端从 TCP 连接读取服务器端发送的响应数据
客户端调用 WAIT FOR PUSH CHANNELS
语句,等待服务器端写入数据到 TCP 连接中。
当服务器端写入数据后,会触发之前提到的客户端事件处理类的 ON_MESSAGE 方法,这个方法会将服务器端响应的字符串内容,保存到事件处理类实例的 message 成员变量中。
这个操作导致逻辑表达式 UNTIL event_handler->message
的值变为 true,因此 ABAP 客户端继续从 WAIT 语句的下一行开始执行。
引入 UP TO 10 SECONDS 即指定 WAIT 的等待时长,最多维持 10 秒。
根据 WAIT 语句的返回值 sy-subrc 判断客户端读取 TCP 连接中的数据是否成功。
- 0 说明成功读取。
- 4 说明当前并没有为 APC 注册事件处理。
- 8 说明 UP TO 指定的 10 秒超时已经到达。
至此这个用 ABAP 编写的 TCP 客户端已经完成,简单测试一下。
执行 DEMO_APC_TCP_CLIENT 报表之后,会看到如下对话框。
TCP_SERVER 显示的是 NCAT 工具的本地路径,然后是本地 IP 地址和端口号。该报表会启动操作系统的 CMD 命令提示行工具,加载 NCAT 工具并监听在这个 IP 地址和端口号,充当 TCP 服务器的角色。
TERMINATOR 0A 即 TCP Frame 终结符的 Unicode 编码,本例我们采用换行符 Line Feed 作为终结符,值为 0A.
MSG 即客户端发送给服务器端的字符串内容。
点击 Enter 按钮,看到 CMD 命令提示行窗口内,已经成功打印出客户端发送过来的 Hello TCP 的字符串,说明通信成功。
此时在 NCAT 的 CMD 窗口内,手动输入一些内容,作为服务器端发送回客户端的数据,比如输入 this is data sent by server.
随即我们在 SAPGUI 的客户端弹出对话框里,收到了这条来自服务器的消息。
如果我们在事件处理类的 on_message 里设置断点,会发现当服务器发送响应数据时,该方法会被 APC 框架触发,并且在调试器里观察收到的数据,确实是以 0A 这个终结符结尾。
后续笔者会介绍更多关于 ABAP Push Channel 的系列内容。
也欢迎大家加入我的知识星球,在里面一起交流 SAP 技术和业务问题。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。