本系列前一篇文章,我们介绍了基于 ABAP Push Channel(简称 APC)技术,使用 ABAP 编程语言来实现 TCP 通讯场景中 TCP 客户端的详细步骤。
使用 ABAP 实现 TCP Socket 编程 (1) - 客户端部分的实现
在基于 TCP 协议的通讯场景中,TCP 客户端是负责发起通信的一方。它通过向服务器端发起连接请求,开始建立一个稳定的通信通道。
客户端根据特定的 IP 地址和端口号找到服务器,并与之建立连接。当连接建立后,客户端可以向服务器发送请求数据并接收服务器的响应。
下图是我们前一篇文章,使用 ABAP 开发好的 TCP 客户端执行后弹出的输入界面。
TCP 服务器的 IP 地址为 XX.XX.117.48, 端口号为 12345.
客户端向服务器端发起的数据为字符串 Hello TCP, answer me!
.
在前文的测试场景里,作为 TCP 服务器,我使用了 NCAT 这个免费的网络工具,它既可以扮演 TCP 服务器,也可以充当 TCP 客户端的角色。
下面图例1 黑色的命题提示行窗口显示了启动后的 NCAT 控制台,已经成功收到了 ABAP 客户端发送过来的 Hello TCP 消息。
然后我在控制台里手动输入 this is data sent by server 并回车,ABAP 客户端立即收到了这条来自 TCP 服务器回复的数据,如下图图例2 所示。
本文我们介绍如何用 ABAP Push Channel 实现一个 TCP 服务器。
TCP 服务器是负责接收连接请求的实体。它会在特定的 IP 地址和端口号上进行监听,等待客户端的连接请求。当一个客户端发起连接时,服务器根据特定的协议规则,即TCP 的三次握手过程来确认和建立 TCP 连接。
之后服务器会处理来自客户端的请求,可能是发送数据、响应查询或其他应用逻辑的处理。
同 ABAP TCP 客户端的开发一样,借助 ABAP Push Channel,开发 ABAP TCP Server 时,ABAP 开发者也无需了解 TCP 底层的 Socket 技术细节。
几乎所有的开发都在 ABAP Class Builder 里完成。
下面是 ABAP TCP Server 的详细开发步骤。
事务码 SAPC,右键创建一个新的 APC 项目,取名 YTCP_TEST:
Connection Type 选择为 TCP Socket,给它维护一个 ABAP 类:YCL_APC_WSP_EXT_YTCP_TEST,名称可以随便取。
在事务码 SICF 里做过开发的朋友们应该对这种 ABAP 类的作用应该不陌生。
同 SICF 里新建一个 ICF node 并给其分配 Handler Class 的思路一致,这里指定的 ABAP 类,就是用来进行诸如 TCP 服务器端的连接建立,通过该连接进行数据读写逻辑等功能的实现。
关于 ABAP SICF 事务码开发的更多介绍,请参阅笔者之前的文章:
从 ABAP Netweaver 的 SICF 到 SAP Kyma 的 Lambda Function
点击上图图例 3 的 Generate Class and Service 之后,系统为我们自动创建该 ABAP 类,同时在 SICF 事务码里也自动创建了一个新的 Service Node,名称为 /sap/bc/apc/sap/ytcp_test.
接下来的任务,就是按部就班实现这个类的方法。
ON_CONNECTION_SETUP
该方法在 TCP 服务器端新收到一个来自客户端发起的 TCP 连接请求时,自动被 APC 框架触发。
在这个方法里,给当前正在建立的 TCP 连接,设置数据读写使用的 TCP Frame 的结束符号为换行符号(缩写为 LF 即 Line Feed),其 16 进制编码为 0A.
method IF_APC_TCP_SERVER_CONFIG~ON_CONNECTION_SETUP.
e_frame-frame_type = if_apc_tcp_frame_types=>co_frame_type_terminator.
e_frame-terminator = '0A'.
endmethod.
ON_START
当 TCP 服务器与客户端成功建立 TCP 连接后,该方法被 APC 框架触发。
此方法里 TCP 服务器可以向客户端发送一些欢迎问候信息。
本例里服务器返回 ON_START has been successfully executed
的调试信息给客户端。
TCP 服务器端发送数据给客户端使用的 API,同本系列前一篇文章里,客户端向服务器端发送数据的 API 一致,本文不再赘述。
使用 ABAP 实现 TCP Socket 编程 (1) - 客户端部分的实现
METHOD if_apc_wsp_extension~on_start.
TRY.
DATA: lv_terminator TYPE xstring VALUE '0A'.
DATA(lo_message) = i_message_manager->create_message( ).
DATA(lv_text_mesage) = |{ sy-mandt }/{ sy-uname }: ON_START has been successfully executed !|.
DATA(lv_binary_message) = cl_abap_codepage=>convert_to( source = lv_text_mesage ).
CONCATENATE lv_binary_message lv_terminator INTO lv_binary_message IN BYTE MODE.
lo_message->set_binary( lv_binary_message ).
i_message_manager->send( lo_message ).
CATCH cx_apc_error INTO DATA(lx_apc_error).
MESSAGE lx_apc_error->get_text( ) TYPE 'E'.
ENDTRY.
ENDMETHOD.
ON_MESSAGE
当 TCP 服务器端接收到客户端发送的消息时,该方法触发,在里面编写回复客户端的逻辑。
METHOD if_apc_wsp_extension~on_message.
TRY.
DATA: lv_terminator TYPE xstring VALUE '0A'.
DATA(lv_text) = i_message->get_text( ).
DATA(lo_message) = i_message_manager->create_message( ).
DATA(lv_text_message) = |{ sy-mandt }/{ sy-uname }: ON_MESSAGE has been successfully executed !|.
DATA(lv_binary_message) = cl_abap_codepage=>convert_to( source = lv_text_message ).
CONCATENATE lv_binary_message lv_terminator INTO lv_binary_message IN BYTE MODE.
lo_message->set_binary( lv_binary_message ).
i_message_manager->send( lo_message ).
" send echo message from client
lo_message = i_message_manager->create_message( ).
lv_text_message = |Echo text from Client:{ lv_text }|.
lv_binary_message = cl_abap_codepage=>convert_to( source = lv_text_message ).
lo_message->set_binary( lv_binary_message ).
i_message_manager->send( lo_message ).
CATCH cx_apc_error INTO DATA(lx_apc_error).
MESSAGE lx_apc_error->get_text( ) TYPE 'E'.
ENDTRY.
ENDMETHOD.
该方法里服务器端接收到客户端的消息后,先后向客户端回复了两条消息。
第一条消息内容为服务器端硬编码的字符串内容,如下图第 8 行代码所示。
第二条消息内容是将客户端发送过来的消息,通过 ON_MESSAGE 输入参数 i_message 的 get_text 方法提取出来之后,再原封不动返回给客户端。
细心的朋友可以观察到,第二条消息发送之前的组装逻辑,没有再像第一条消息那样,手动将 LF 换行符添加到消息末尾。
这样处理的原因,在调试器里能观察得很清楚。假设客户端只发送了一个感叹号过来,调试器里观察到,感叹号后已经
包含了客户端组装好的 LF 换行符的 16 进制编码 0A,因此服务器端直接利用这个终止符就行了,无需重复添加。
至此 ABAP 编码工作已经完成。
在 SAPC 事务码里,将 YTCP_TEST 这个 APC 项目激活。
在事务码 SICF 里将上图的 ICF 节点 ytcp_test 也激活。
为了让这个实现好的 TCP 服务器能够在指定的端口上监听,我们还需要在 ABAP 应用服务器上设置一个 TCP 端口。这个步骤通过事务码 SMICM 完成。
笔者之前的文章介绍过 SAP ICM 这个组件。ICM 是 SAP NetWeaver 应用服务器的一部分,起到了网关的作用,管理着 SAP 系统内部和外部之间的数据流。
通过 ICM,SAP 系统可以处理不同类型的互联网请求,如网页浏览器的 HTTP 请求、TCP 客户端的数据请求,Web 服务调用,或者通过 SMTP 发送电子邮件。
ICM 通过支持各种协议,实现了 SAP 系统的跨平台交互功能。
- 我的学习笔记 (3) - ABAP STRUST 事务码和背后的基础知识
- 授人以鱼不如授人以渔:记录一次使用 ABAP 调用 OData 服务,遇到 401 Unauthorized 错误的详细排查过程
出于安全性考虑,我们不希望将 TCP 服务器对应的 ICF 节点的完整路径,即 /sap/bc/apc/sap/ytcp_test 暴露给外部,所以首先在 SICF 事务码里给其维护一个 External Alias 即别名。
创建好的别名如下图所示。External Alias 的语义是,TCP 客户端可以通过访问短链接 /ytcp_test
, 来达到访问其完整路径的相同效果。
接下来使用事务码 SMICM,给上面创建的短链接 ytcp_test
, 创建一个新的 TCP 服务。
新创建的 TCP 服务的端口号设置成 12345,协议设置为 TCP,TCP url 值为刚刚创建的 External Alias 即 /ytcp_test
.
在 ICM 的服务监控器里,看到了这个成功创建的 TCP 服务。
测试
我们仍旧使用前文提到的网络工具 NCAT, 使用命令行向实现好的 ABAP TCP Server 发起连接请求。
命令行第一个参数是 ABAP TCP Server 所在 ABAP 应用服务器的主机名,12345 是我们刚刚在 SMICM 里创建的 TCP 服务的端口号。
连接成功后,TCP 客户端会收到来自服务器端发送的连接成功的欢迎消息:ON_START has been successfully executed 也就是我们在 ABAP TCP 服务器实现的 ON_START 方法里,硬编码的字符串。
接着在 NCAT 客户端的控制台,手敲 send first sentence 然后回车(如下图图例 1)。
回车之后,会收到从服务器 ON_MESSAGE 的 ABAP 代码里,组装好的两条回复消息,如下图图例2 和图例3 所示。
用 APC 实现的 ABAP 服务器和客户端的调试也没有什么特别之处。本例在 TCP 服务器实现类的 ON_MESSAGE 里设置断点,然后使用 NCAT 客户端发送数据,断点即触发。
笔者后续将介绍 ABAP Push Channel 相关的更多技术,比如 Web Socket 和 ABAP Messaging Channel, 欢迎大家持续关注。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。