c - lwip 板无法保持与另一个 lwip 板的连接
问题描述
我有一个奇怪的问题。一段时间以来,我一直在尝试更换一个小型协议转换器(基本上是双向串行到以太网......主从),我已经获得了具有更多功能的东西。
背景故事
经过大量的逆向工程,我发现了设备的工作原理,我一直在尝试复制它,并且我已经成功地将我的电路板连接到设备......我尝试将原始设备连接为主机和我的板作为从属,反之亦然,一切正常,实际上更好,因为在更高的速度下没有更多的数据包丢失(连接2个原始数据包会导致数据包丢失)。
但是,当我尝试将我的设备连接为主设备,将另一台设备连接为从设备时..运行完全相同的代码,它适用于 2 或 3 次交换,然后它停止......最终有时会在几分钟后再次尝试 2或 3 次以上。
测试是如何进行的
- 我连接了一个 modbus 主机和从机(modbustools,两个不同的实例)。主机是串行RTU modbus,从机是串行RTU modbus;
- 我将我的一台设备配置为主机并将其连接到串行端口,以便它接收串行 modbus 并将协议发送到与其连接的设备;
- 我配置我的从机,使其通过串行端口连接到从机 modbus。基本上,它通过创建一个套接字并连接到主机的 IP 来工作,然后它通过以太网等待主机传输,通过串行将其发送到从机 modbus(modbustools),接收响应,将其发送给它的主机,然后将其发送到modbus 主机(modbustools);
我有点困惑,但这就是它的工作原理......我的主人等待套接字连接,然后它们之间的通信开始,因为这是旧的工作方式。
我现在写了一个回显客户端来测试连接。基本上现在,我的代码连接到服务器(我的主服务器),它接收一个数据包,然后回复它收到的相同数据包。当我尝试将其连接到我的 2 块板时,它们不起作用。它更相似,2 或 3 次交换然后它停止,但是当我将它连接到原始设备时,它继续运行顺利。
来源
这是我的 TCP 主机(实际上是服务器)初始化:
void initClient() {
if(tcp_modbus == NULL) {
tcp_modbus = tcp_new();
previousPort = port;
tcp_bind(tcp_modbus, IP_ADDR_ANY, port);
tcp_sent(tcp_modbus, sent);
tcp_poll(tcp_modbus, poll, 2);
tcp_setprio(tcp_modbus, 128);
tcp_err(tcp_modbus, error);
tcp_modbus = tcp_listen(tcp_modbus);
tcp_modbus->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_modbus->keep_intvl = 1000; // sends keep-alive every second
tcp_accept(tcp_modbus, acceptmodbus);
isListening = true;
}
}
static err_t acceptmodbus(void *arg, struct tcp_pcb *pcb, err_t err) {
tcp_arg(pcb, pcb);
/* Set up the various callback functions */
tcp_recv(pcb, modbusrcv);
tcp_err(pcb, error);
tcp_accepted(pcb);
gb_ClientHasConnected = true;
}
//receives the packet, puts it in an array "ptransparentmessage->data"
//states which PCB to use in order to reply and the length that was received
static err_t modbusrcv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
if(p == NULL) {
return ERR_OK;
} else if(err != ERR_OK) {
return err;
}
tcp_recved(pcb, p->len);
memcpy(ptransparent.data, p->payload,p->len);
ptransparent->pcb = pcb;
ptransparent->len = p->len;
}
串行接收基本上是这样的:检测接收到的一个字节,开始超时,当超时结束时发送通过已经连接到服务器的 TCP 套接字接收到的任何内容.. 然后它通过 acceptmodbus 函数接收数据包并通过串行端口发送它.
这是我的客户(从)代码:
void init_slave() {
if(tcp_client == NULL) {
tcp_client = tcp_new();
tcp_bind(tcp_client, IP_ADDR_ANY, 0);
tcp_arg(tcp_client, NULL);
tcp_recv(tcp_client, modbusrcv);
tcp_sent(tcp_client, sent);
tcp_client->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_client->keep_intvl = 100; // sends keep-alive every 100 mili seconds
tcp_err(tcp_client, error);
err_t ret = tcp_connect(tcp_client, &addr, portCnt, connected);
}
}
其余代码相同。唯一改变的是操作流程。
- 连接到服务器
- 等待数据包
- 通过串口发送
- 等待响应超时(与服务器相同的超时,它只是以不同的方式开始计数......服务器在收到一个字节后启动,客户端在通过串口发送内容后启动)
- 获取响应并将其发送到服务器
观察:
通信中未检测到错误。经过一些测试,它似乎并不是导致挂起的交换次数。它会在一段时间后发生。在我看来,这听起来像是断开连接问题或超时错误,但没有发生断开连接,也没有收到更多数据包。当我停止调试并检查套接字时,没有检测到任何异常。
解决方案
如果我以正确的方式理解您的问题,那么您有一台带有两个串行端口的计算机,每个都运行一个 Modbus 客户端和服务器实例。然后,您可以从这些端中的每一端转到您的 STM32 板,这些板在其串行端口上接收数据并转发到将它们相互连接的以太网上的 TCP。
不容易说,但根据您描述的症状,您肯定有一个或多个超时问题,可能在串行方面。我认为在不测试代码的情况下帮助您查明代码的确切问题并不容易,如果您不能展示完整的功能部分,当然也不会如此。
但是您可以改进很多的是您在终端方面进行调试的方式。您可以尝试用可以提供更多详细信息的东西替换modbustools 。
获取额外调试信息的最简单的解决方案是使用pymodbus,您只需安装库pip
并使用示例提供的客户端和服务器。您需要的唯一修改是将它们更改为串行接口注释和取消注释几行。这将为您提供非常有用的调试细节。
如果您的计算机上有 C 开发环境,最好选择libmodbus。这个库有一组很棒的单元测试。同样,您只需编辑代码以设置串行端口的名称并运行服务器和客户端。
最后,我不知道这在多大程度上对您有用,但您可能想看看SerialPCAP。使用此工具,您可以点击 RS-485 总线并查看其上运行的所有查询和响应。我想你有 RS-232,它是点对点的,不能与总线上的三个设备一起工作。如果是这样,您可以尝试端口转发。
编辑:更仔细地阅读你的问题,我发现这句话特别麻烦:
...检测接收到的一个字节,开始超时,当超时结束时发送通过已经连接到服务器的 TCP 套接字接收到的任何内容...
为什么需要引入这种人为的延迟?在 Modbus 中,您有非常明确的包,您可以通过最小 3.5 帧间距来识别,这就是您所说的timeout吗?
不相关,但我还记得有一个包含pymodbus的串行转发器示例,它可能会以某种方式帮助你(也许你可以用它来模拟你的一块板?)。
推荐阅读
- xcode - 如何找到多视图控制器的编辑器
- html - 如何显示html div,以便在表单提交后自动显示错误消息
- emacs - 如何根据起始字符设置emacs线条颜色
- intellij-idea - SonarQube v5.6.6 Rest API - 权限不足
- selenium - 如何发现在聚合物测试期间使用了哪个 selenium 和 chromedriver
- javascript - 错误:Firebase 的 Cloud Functions 生成 EACCES(ghostscript)
- hyperledger-composer - Heprledger Composer REST:端点中的旧模型
- react-native - 为什么闪屏没有在本机反应中显示?
- twitter-bootstrap - 在外部单击时关闭图标弹出框
- crashlytics - 适用于多种环境的 Fabric Crashlytics - Android