【发布时间】:2020-11-19 05:43:23
【问题描述】:
我无法调整 TCP 客户端-服务器通信。
我当前的项目有一个客户端,在 PC (C#) 和服务器上运行, 在嵌入式 Linux 4.1.22-ltsi 上运行。 它们使用 UDP 通信来交换数据。
客户端和服务器工作在阻塞模式,并且 发短信一到二 (16、60、200 字节等),包括命令或参数集。
消息确实包含任何带有消息长度的标头,因为
UDP是面向消息的协议。它的recvfrom() API 返回接收到的字节数。
因为我的服务器的程序结构对于获取和处理整个单独的消息很重要。
当我尝试实现 TCP 通信类型而不是 UDP 时出现问题。
服务器的接收缓冲区(recv() TCP API)为 2048 字节:
#define UDP_RX_BUF_SIZE 2048
numbytes = recv(fd_connect, rx_buffer, UDP_RX_BUF_SIZE, MSG_WAITALL/*BLOCKING_MODE*/);
所以,recv() API 在rx_buffer 已满时(即在它接收到之后)从等待中返回
2048 字节。它打破了所有程序方法。换句话说,当客户端发送 16 字节命令时
到服务器并等待它的回答,服务器的recv() 保留消息
“在胃里”,直到它收到 2048 个字节。
我尝试如下修复它,但没有成功:
-
在客户端 (C#) 我设置了套接字参数
theSocket.NoDelay。 当我在嗅探器上检查这个时,我看到客户端“如我所愿”发送消息, 符合要求的长度。 -
在服务器端,我将 TCP_NODELAY 套接字选项设置为 1
int optval= 1;
setsockopt(fd,IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval);
- 在服务器端 (Linux) 我检查了套接字选项
SO_SNDLOWAT/SO_RCVLOWAT,它们每个都是 1 个字节。
请查看随附的嗅探器日志图片。 10.0.0.10 是客户端。 10.0.0.106 是服务器。可以看出,客户端激活了 PSH 标志(push),通知服务器端将传入的数据立即移动到应用程序,并且不填充缓冲区。
附加问题:什么是在双方之间运行的 SSH 加密数据包。我想这是我在 PC 上的 Eclipse 调试器(通过相同的以太网连接运行服务器应用程序)发送它们。我说的对吗?
所以,我的问题是如何让 `recv() API 返回每条短消息(16、60、200 字节等),而不是在接收缓冲区填满之前累积它们。
【问题讨论】:
-
我有点困惑。您似乎明白,您不需要带有消息长度的标头的唯一原因是 UDP 是一种面向消息的协议。既然 TCP 不是,为什么你认为你仍然不需要带有消息长度的标头?
-
'服务器的
recv()将消息保存在“肚子里”,直到它收到 2048 个字节'。这只是因为您指定了MSG_WAITALL,与您的评论相反,它不是“阻塞模式”,它告诉 TCP 尝试填充整个缓冲区。解决方法:不要使用。 -
@Marquis of Lorne,“客户端和服务器在阻塞模式下工作”——这是我帖子的引述。如果我将选项设置为 MSG_DONTWAIT,recv() 将立即返回,这不是我的方法。
-
@Yakov 1.
MSG_WAITALL代表阻塞模式的声明就在您代码的注释中。 2.我的评论中没有关于使用MSG_DONTWAIT的内容。你编的。也不要用那个。尝试 0。