【问题标题】:I can not sent short messages by TCP protocol我无法通过 TCP 协议发送短消息
【发布时间】: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 个字节。

我尝试如下修复它,但没有成功:

  1. 在客户端 (C#) 我设置了套接字参数theSocket.NoDelay。 当我在嗅探器上检查这个时,我看到客户端“如我所愿”发送消息, 符合要求的长度。

  2. 在服务器端,我将 TCP_NODELAY 套接字选项设置为 1

int optval= 1;
setsockopt(fd,IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval);
  1. 在服务器端 (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。

标签: sockets tcp udp


【解决方案1】:

TCP 是面向连接的,它还维护数据包的发送和接收顺序。

话虽如此,在 TCP 客户端中,您将收到字节流,而不是 UDP 中的单个 udp 消息。因此,您需要将数据包长度和标记作为初始字节发送。

所以客户端可以先找到数据包长度,然后读取数据,直到达到数据包长度,然后期待新的数据包长度。

您还可以检查诸如 netty、zmq 之类的库来完成这项额外工作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 2016-08-21
    • 2019-03-31
    相关资源
    最近更新 更多