为啥我的workerman作为TCP客户端连接PLC,设置tcp_nodelay不生效,要解决怎么?

zgh419566

TCP客户端代码如下:
截图

程序启动后,逐个向服务器设备发送数据
截图

问题:代码中明明分开发的数据,在抓包时发现内容会自动连接在一起后才发出去。
收数据也一样,明明分开拿 数据,会被合成一起才送给应用程序。
截图

查了PHP环境支持的
<?php
echo function_exists('socket_import_stream');

php test_socket_import_stream.php
1

是否需要修改哪里的配置才能解决这个问题?

[www@linux beckhoff_ads]$ php start_ads_client_wkm.php start
Workerman[start_ads_client_wkm.php] start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:4.0.41          PHP version:7.4.28           Event-Loop:\Workerman\Events\Event
-------------------------------------------- WORKERS ---------------------------------------------
proto   user            worker          listen          processes    status           
tcp     www             none            none            1             [OK]            
--------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
2022-08-01 06:15:28 Send 000020000000c0a8013401012103c0a801640101228004000400000000000000000000000000
2022-08-01 06:15:28 Send 00002c000000c0a801340101c800c0a8016401012180020004000c0000000000000001000000010000002100000010000000
2022-08-01 06:15:28 Send 000020000000c0a8013401012003c0a801640101218001000400000000000000000002000000
2022-08-01 06:15:28 Send 00002c000000c0a8013401012003c0a8016401012180010005000c0000000000000003000000010000002100000010000000
2022-08-01 06:15:28 Send 000021000000c0a8013401012103c0a80164010122800150000001000000000000000400000001
2022-08-01 06:15:28 Send 000021000000c0a8013401012103c0a80164010122800150000001000000000000000500000011
2022-08-01 06:15:28 Send 000022000000c0a8013401012103c0a8016401012280015000000200000000000000060000005112
2022-08-01 06:15:28 Send 000022000000c0a8013401012103c0a8016401012280015000000200000000000000070000005110
2022-08-01 06:15:28 Send 000022000000c0a8013401012103c0a8016401012280015000000200000000000000080000003800
2022-08-01 06:15:28 Send 0000a0000000c0a8013401012103c0a8016401012280015000008000000000000000090000005014050000000400480000000100000004004d00000006000000f9ff2200080000000100000000000400160000002800000004005100000002000000f9ff2200080000000100000000000400160000002800000004005100000002000000f9ff2200080000000100000000000400160000002800000004005100000002000000
2022-08-01 06:15:28 Connect Ok
2022-08-01 06:15:28 recv 000028000000c0a8016401012280c0a8013401012103040005000800000000000000000000000000000005000000000038000000c0a8016401012180c0a801340101c80002000500180000000000000001000000000000001000000050000000102700000100000000000000000038000000c0a8016401012180c0a80134010120030100050018000000000000000200000000000000020b3a08504c4320536572766572000000000000

2022-08-01 06:15:28 recv 0000ee000000c0a8016401012280c0a801340101210301500100ce0000000000000004000000e803e8030000000000003080000000ff00000000010000ff0000881300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026000000c0a8016401012280c0a8013401012103015001000600000000000000050000000000c0990400000024000000c0a8016401012280c0a80134010121030150010004000000000000000600000000001200000061000000c0a8016401012280c0a80134010121030150010041000000000000000700000000001000000000000000000000000000000000cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd000000000000000000000000000000000000cdcdcdcd00000022000000c0a8016401012280c0a8013401012103015001000200000000000000080000000000

2022-08-01 06:15:28 recv 000024000000c0a8016401012280c0a80134010121030150010004000000000000000900000000001400
1065 2 1
2个回答

walkor

tcp协议是基于流的,不会自动将请求分开。收到的数据可能是一个请求的部分数据,也可能是多请求数据连在一起。
使用tcp时需要用应用层协议从数据流里区分请求边界,这就是为什么有了tcp,还要有http ftp smtp这些应用层协议。
tcp_nodelay 也不是用于分割请求的

  • zgh419566 2022-08-02

    根据网上的参考资料,开启tcp_nodelay以后,执行send指令数据会马上送出去,收到数据,程序会马上产生onmessage事件。 ( https://www.cnblogs.com/zhangkele/p/9494280.html) 目前我们使用workerman做工业控制 ,连接倍福(beckhoff)PLC ,我再研究一下。感谢walkor.

  • zgh419566 2022-08-02

    beckhoff官方的C++驱动程序默认开启了tcp_nodelay,可以做到数据即时收发的。 https://github.com/Beckhoff/ADS/blob/master/AdsLib/Sockets.cpp (line 229 setsockopt )。 而swoole也有这个特性 , workerman比swoole简单,所以才选择的这套方案。
    我看了workerman的源码,也有这个特性,worker.php line 2296,但是看样子设置没有生效,麻烦walkor再帮解答一下。

  • zgh419566 2022-08-02

    再此表达一下,workerman的tcp reconnect功能 很好使,网络连接断开以后会自动重连,而swoole不一样,连不上就直接报错了,connected状态也依然为true,所以才选择的workerman。

  • walkor 2022-08-02

    tcp_nodelay应该是生效了的,只不过 tcp_nodelay 无法从根本上解决你的问题,tcp的特性就是这样,数据没有边界,需要接收端从数据流里将完整的数据提取出来。

    快速发送数据或者网络延迟或者对端接收数据不够快都会导致数据在socket缓冲区积压,数据就连在一起了。

  • zgh419566 2022-09-17

    感谢walkor的分享,我已经找到了原因:我在定时器里面执行了一大堆send,就算操作系统的底层收到数据也没有机会执行onMessage回调,因为我定时器里面的函数没有执行完。操作系统收到数据以后一直将数据放到了缓冲区里面。直到定时器里面的函数执行完成以后,检查baseRead时,才会回调onMessage。
    解决办法:每次执行完send指令以后,执行这一句检查是否收到了数据,如果有回复数据就触发OnMessage进行检查,否则继续发送下一条数据。$conn->baseRead($conn->getSocket());
    通过这一句处理问题,我认真研究了workerman的底层源码,之前只停留在会用,这次进步了,特此将该问题分析共享出来。

  • zgh419566 2022-09-17

    以上这种方式是否符合解决问题的机制,麻烦walkor帮评估一下。

  • zgh419566 2022-09-17

    我这样的操作,是否将异步变成了同步……,总体的思路就是:发一点,检查是否收到了回复。

  • zgh419566 2022-09-17

    发送一个我之前代码的bug,之前我发送一次数据,检查一次接收,这样是有问题的,因为有可能执行一次baseRead不能一次性返回所有的接收数据,因为还需要调用 getRecvBufferQueueSize() 做一个判断,只有把数据取完以后再执行其他内容。

  • walkor 2022-09-18

    发送端和接收端都有socket缓冲区,如果发送端发送多个数据,数据有积压在发送端socket缓冲区的情况,数据还是连在一起发送到对端的。除非是发送端只发送一个数据包,然后等对端响应一个包。

ysxpark

里面不是有个text协议吗,用/n分包挺好用啊

  • zgh419566 2022-08-07

    感谢支持,但是对端TCP服务器是PLC硬件,协议里面定义的方式就是这样。

年代过于久远,无法发表回答
🔝