jasonj333

V1

2022/02/09阅读:46主题:红绯

CAPL脚本如何实现TCP Socket通信

以下示例来自CANoe DEMO,路径为C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 12.0.101\IO_HIL\TCP_IP

Server TCP

由于TCP连接是服务器的tcp socket在listen状态下接受连接请求,从而完成三次握手操作

所以,首先服务器应该先创建好tcp socket后,绑定本地端口,然后处于监听状态,这样才能随时接受客户端的连接请求

CANoe DEMO里关于TCP连接通信是在面板上完成的

这个面板有TCP和UDP两种通信,UDP通信不需要建立连接,比较简单,这里就不分析了

server端tcp首先需要填入本地的IP地址和监听的端口号,然后通过点击“Start listening”,完成从创建tcp socket到绑定、监听和接受连接请求的一系列动作

我们看看是如何用capl实现的

Server端创建tcp套接字,绑定、监听并接受连接请求

  1. 点击按钮“Start listening”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 按下去,会执行里面的函数StartListenTcp

可以看出,端口号用的是面板上的,IP地址却没有使用面板上的,选择IP地址变量gIpAddress,跳到定义的地址,发现它定义时是INVALID_IP,0xffffffff

定义IP地址变量时赋值为无效值的好处是,这样使用时可以通过判定是不是无效值来判断是否操作成功,同样的还有socket句柄,定义时先赋值为无效套接字,然后在创建后,如果还是无效值,就说明没有创建成功

这里IP地址被赋值无效IP,然后又没有使用面板上的值,就拿来创建tcp socket,肯定是不对的

所以肯定是在我们点击按钮之前,capl已经实现了读取正确的IP地址了

从CANoe运行到点击按钮,只有on start回调函数里有实现的可能

CANoe运行时会执行函数SetupIp

可以看出,IP地址是从网卡上自动获取的,然后显示到面板上,这些是在CANoe运行起来的时候就完成的操作

然后我们回到上面的函数StartListenTcp,有了IP和Port,接着就是创建tcp socket,调用TcpListen,让主动套接字转为被动套接字,监听外部的连接请求

  1. 触发回调函数OnTcpListen

如果有连接请求,虽然监听到了,但是没有接受的动作啊,就会阻塞在那

所以我们应该调用TcpAccept函数来接受连接请求,那为什么不在上面的TcpListen后直接调用TcpAccept呢

因为这样只能接受一个连接请求,而对于服务器来说,套接字是处于监听状态下,我希望的是过来一个连接请求,我就接受一个,可以实现接受多个连接请求,也就是多次调用TcpAccpet

我们知道python可以通过while True的循环来不停地接受多个连接请求,而capl则是通过回调函数OnTcpListen

OnTcpListen,回调函数,在调用TcpListen函数后,接收到客户端的连接请求时,就会调用它

这就意味着,服务器的tcp socket在listen以后,只要监听到有连接请求,就会自动触发OnTcpListen的执行

那我们就可以在OnTcpListen里实现接受连接请求的操作,也就是调用TcpAccept函数

Server端tcp建立连接后自动接收数据

这里采用的是调用TcpAccept函数成功后,跟着调用了TcpReceive。相当于是tcp建立连接后就开始不断地接收数据

  1. 调用函数TcpRecv
  1. 触发回调函数OnTcpReceive

虽然上面调用了一次TcpReceive函数,但是数据只能接收一次。我们想实现的是,只要有客户端的数据发过来,服务器的gTcpDataSocket就可以把数据接收,存入缓存

所以还需要用回调函数OnTcpReceive实现只要有数据进来,就接收的操作

OnTcpReceive,回调函数,tcp套接字上的接收操作完成时调用,也就是执行TcpReceive函数接收到数据时调用这个回调函数

所以,现在你知道面板上Client IP和Port是干嘛用的了吧,就是可以获取对方的IP和Port,然后显示到面板上

为什么在OnTcpReceive里还要执行TcpReceive呢?是为了循环接收数据。如果没有它,那么接收到数据时触发回调函数OnTcpReceive执行一次,就没了

Server端tcp建立连接后发送数据

  1. 点击按钮“Send data to client”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 然后会执行里面的函数SendTcpData

对于服务器来说,用的是连接成功后新的套接字发送数据

  1. 触发回调函数OnTcpSend

这里还实现了OnTcpSend回调函数,不过我觉得要不要这个回调函数都无所谓,它实现的功能也就是判断下实际发送数据的套接字是不是执行TcpSend的套接字,顺便看看是否发送成功

Server端关闭套接字

  1. 点击按钮“Stop listening”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 执行里面的函数StopListenTcp

这里用TcpClose关闭两个套接字,关闭gTcpDataSocket这个套接字,其实是断开这个tcp连接,关闭这个套接字并不会影响“被动”套接字gTcpSocket监听其他连接请求。而关闭gTcpSocket,是把监听的“被动”套接字也给关闭了,这样就没法监听客户端的连接请求了

Client TCP

tcp建立连接,是以客户端的主动发起连接请求开始的,所以客户端TCP面板上应该有一个请求连接的button

客户端向服务器发起连接请求,必须知道服务器的IP地址和端口号,所以server那一行的IP和Port就是目的IP和Port

Client端创建tcp套接字,绑定并发送连接请求

  1. 点击按钮“Connect to server”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 调用函数ConnectTcp

从上面可以看出

客户端绑定的端口(ip, port)中的源IP不是从面板上获取的,那么肯定和服务器的一样,也是当CANoe运行时从网卡上自动获取的IP地址,且显示到面板上

从下面的代码可以看出:

用TcpOpen创建的socket,赋值给了gTcpDataSocket,表明连接成功后就是用这个套接字传输数据。并没有服务器那种“被动”套接字用来监听

Client端连接成功后自动接收数据

客户端连接成功后直接让其开始接收数据,这里的代码和服务器那边接收数据的代码一样

  1. 调用函数TcpRecv
  1. 触发回调函数OnTcpReceive的执行

Client端发送数据

发送数据的代码也和服务器的差不多

  1. 点击按钮“Send data to server”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 调用函数SendTcpData

这里证明了我上面的猜测,上面确认有一部分代码是错误的

  1. 触发回调函数OnTcpSend的执行

Client端关闭套接字

只有一个套接字,所以只需要关闭gTcpDataSocket

  1. 点击按钮“Disconnect from server”

将触发这个按钮关联的系统变量的回调函数的执行

  1. 调用函数DisconnectTcp

客户端和服务器还分别实现了当CANoe停止运行时,关闭套接字并初始化

这是客户端的,服务器的还要加上gTcpSocket的代码


分类:

后端

标签:

后端

作者介绍

jasonj333
V1