草庐IT

c++ - 两次读取 UDP 套接字会丢弃第一次调用后剩余的字节

coder 2024-02-11 原文

我的目标是分两步从 UDP 套接字读取数据。问题 是如果我向套接字写入的数据多于第一步读取的数据。结果是 剩余数据消失。

我将我的代码缩减为以下片段:

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[2];

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;


  net_sock.close();
  return EXIT_SUCCESS;
}

当我向套接字写入数据时如下:

echo '0123456789' | nc -u localhost 1234

程序输出前两个字节 01 然后阻塞。 相反,我期望输出:

01
23

但是,它会在第二次 receive() 调用时阻塞。根据 manual:

The call will block until one of the following conditions is true:

∙ The supplied buffers are full. […]
∙ An error occurred.

为什么缓冲区是空的?如果我启动

echo '0123456789' | nc -u localhost 1234

第二次,receive() 调用解锁,但也输出 01。在哪里 我第一次输入的剩余数据 23456789 是否消失了,我如何在后续访问它 receive() 调用?

一些背景:用例是读取一个可变长度的数据包, 这意味着先读取标题,然后再读取 处理 header (其中包括有关数据包长度的信息) 继续阅读有效负载。

最佳答案

UDP 是一种面向数据包的协议(protocol)。您必须一次读取整个数据包。

这方面的技术术语是“消息边界保留”。这意味着它将在一个 block 中发送来自单个 writesend 操作的所有数据。当您readrecv 时,它会从特定 block 中获取数据,然后丢弃该 block ,再也不会出现。

这是设计使然。

您应该在您的协议(protocol)中确定最大数据包长度,然后始终读取该长度。如果写入的长度小于该长度,读取的字节数将少于要求的字节数。

此外,请记住,在 UDP 中不能保证任何给定的 block 都会到达。因此,如果您同时计数,您的读取次数可能不如写入次数多。

像这样:

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

const unsigned int max_datagram_size = 65536;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[max_datagram_size];

  // I like declaring all values that I do not expect to change as const.
  const int recved_size = net_sock.receive( buffer( data, max_datagram_size ) );
  if (recved_size >= 0) {
    std::cout << ::std::string(data, recved_size) << '\n';
  } else {
    std::cout << "There was some sort of error receiving data.\n";
  }


  net_sock.close();
  return EXIT_SUCCESS;
}

关于c++ - 两次读取 UDP 套接字会丢弃第一次调用后剩余的字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15523849/

有关c++ - 两次读取 UDP 套接字会丢弃第一次调用后剩余的字节的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  5. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  6. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  7. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  8. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  9. ruby - 是否可以在不实际发送或读取数据的情况下查明 ruby​​ 套接字是否处于 ESTABLISHED 或 CLOSE_WAIT 状态? - 2

    s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成

  10. ruby - 我怎样才能只写一次 "Text"并同时检查 path_info 是否包含 'A' ? - 2

    -if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc

随机推荐