Muduo 网络编程示例之一:五个简单 TCP 协议

Muduo 网络编程示例之零:前言 我将会写一系列文章,介绍用 muduo 网络库完成常见的 TCP 网络编程任务。这些例子都比较简单,逻辑不复杂,代码也很短,适合摘取关键部分放到博客上。其中一些有一定的代表性与针对性,比如“如何传输完整的文件”估计是网络编程的初学者经常遇到的问题。 阅读详情

陈硕 (giantchen_AT_gmail)

Blog.youkuaiyun.com/Solstice

这是《Muduo 网络编程示例》系列的第一篇文章。

全系列文章列表: http://blog.youkuaiyun.com/Solstice/category/779646.aspx

本文将介绍第一个示例:五个简单 TCP 网络服务协议,包括 echo (RFC 862)、discard (RFC 863)、chargen (RFC 864)、daytime (RFC 867)、time (RFC 868),以及 time 协议的客户端。各协议的功能简介如下:

  • discard - 丢弃所有收到的数据;
  • daytime - 服务端 accept 连接之后,以字符串形式发送当前时间,然后主动断开连接;
  • time - 服务端 accept 连接之后,以二进制形式发送当前时间(从 Epoch 到现在的秒数),然后主动断开连接;我们需要一个客户程序来把收到的时间转换为字符串。
  • echo - 回显服务,把收到的数据发回客户端;
  • chargen - 服务端 accept 连接之后,不停地发送测试数据。

以上五个协议使用不同的端口,可以放到同一个进程中实现,且不必使用多线程。完整的代码见 muduo/examples/simple,下载地址 http://muduo.googlecode.com/files/muduo-0.1.6-alpha.tar.gz 。

discard

Discard 恐怕算是最简单的长连接 TCP 应用层协议,它只需要关注“三个半事件”中的“消息/数据到达”事件,事件处理函数如下:

   1: void DiscardServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
   2:                  muduo::net::Buffer* buf,
   3:                  muduo::Timestamp time)
   4: {
   5:   string msg(buf->retrieveAsString());  // 取回读到的全部数据
   6:   LOG_INFO << conn->name() << " discards " << msg.size() << " bytes at " << time.toString();
   7: }
剩下的都是例行公事的代码:
定义一个 DiscardServer class,以 TcpServer 为成员。
   1: #ifndef MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H
   2: #define MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H
   3:  
   4: #include 
   5:  
   6: // RFC 863
   7: class DiscardServer
   8: {
   9:  public:
  10:   DiscardServer(muduo::net::EventLoop* loop,
  11:                 const muduo::net::InetAddress& listenAddr);
  12:  
  13:   void start();
  14:  
  15:  private:
  16:   void onConnection(const muduo::net::TcpConnectionPtr& conn);
  17:  
  18:   void onMessage(const muduo::net::TcpConnectionPtr& conn,
  19:                  muduo::net::Buffer* buf,
  20:                  muduo::Timestamp time);
  21:  
  22:   muduo::net::EventLoop* loop_;
  23:   muduo::net::TcpServer server_;
  24: };
  25:  
  26: #endif  // MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H
注册回调函数
   1: DiscardServer::DiscardServer(muduo::net::EventLoop* loop,
   2:                              const muduo::net::InetAddress& listenAddr)
   3:   : loop_(loop),
   4:     server_(loop, listenAddr, "DiscardServer")
   5: {
   6:   server_.setConnectionCallback(
   7:       boost::bind(&DiscardServer::onConnection, this, _1));
   8:   server_.setMessageCallback(
   9:       boost::bind(&DiscardServer::onMessage, this, _1, _2, _3));
  10: }
  11:  
  12: void DiscardServer::start()
  13: {
  14:   server_.start();
  15: }
处理连接与数据事件
   1: void DiscardServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
   2: {
   3:   LOG_INFO << "DiscardServer - " << conn->peerAddress().toHostPort() << " -> "
   4:     << conn->localAddress().toHostPort() << " is "
   5:     << (conn->connected() ? "UP" : "DOWN");
   6: }
   7:  
   8: void DiscardServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
   9:                  muduo::net::Buffer* buf,
  10:                  muduo::Timestamp time)
  11: {
  12:   string msg(buf->retrieveAsString());
  13:   LOG_INFO << conn->name() << " discards " << msg.size() << " bytes at " << time.toString();
  14: }
在 main() 里用 EventLoop 让整个程序转起来
   1: #include "discard.h"
   2:  
   3: #include 
   4: #include 
   5:  
   6: using namespace muduo;
   7: using namespace muduo::net;
   8:  
   9: int main()
  10: {
  11:   LOG_INFO << "pid = " << getpid();
  12:   EventLoop loop;
  13:   InetAddress listenAddr(2009);
  14:   DiscardServer server(&loop, listenAddr);
  15:   server.start();
  16:   loop.loop();
  17: }

daytime

Daytime 是短连接协议,在发送完当前时间后,由服务端主动断开连接。它只需要关注“三个半事件”中的“连接已建立”事件,事件处理函数如下:

   1: void DaytimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
   2: {
   3:   LOG_INFO << "DaytimeServer - " << conn->peerAddress().toHostPort() << " -> "
   4:     << conn->localAddress().toHostPort() << " is "
   5:     << (conn->connected() ? "UP" : "DOWN");
   6:   if (conn->connected())
   7:   {
   8:     conn->send(Timestamp::now().toFormattedString() + "/n"); // 发送时间字符串
   9:     conn->shutdown(); // 主动断开连接
  10:   }
  11: }

剩下的都是例行公事的代码,为节省篇幅,此处从略,请阅读 muduo/examples/simple/daytime。

用 netcat 扮演客户端,运行结果如下:

$ nc 127.0.0.1 2013
2011-02-02 03:31:26.622647    # 服务器返回的时间字符串

time

Time 协议与 daytime 极为类似,只不过它返回的不是日期时间字符串,而是一个 32-bit 整数,表示从 1970-01-01 00:00:00Z 到现在的秒数。当然,这个协议有“2038 年问题”。服务端只需要关注“三个半事件”中的“连接已建立”事件,事件处理函数如下:

   1: void TimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
   2: {
   3:   LOG_INFO << "TimeServer - " << conn->peerAddress().toHostPort() << " -> "
   4:     << conn->localAddress().toHostPort() << " is "
   5:     << (conn->connected() ? "UP" : "DOWN");
   6:   if (conn->connected())
   7:   {
   8:     int32_t now = sockets::hostToNetwork32(static_cast<int>(::time(NULL)));
   9:     conn->send(&now, sizeof now);  // 发送 4 个字节
  10:     conn->shutdown();  // 主动断开连接
  11:   }
  12: }

剩下的都是例行公事的代码,为节省篇幅,此处从略,请阅读 muduo/examples/simple/time。

用 netcat 扮演客户端,并用 hexdump 来打印二进制数据,运行结果如下:

$ nc 127.0.0.1 2037 | hexdump -C
00000000  4d 48 d0 d5                                       |MHÐÕ|
00000004

 

time_client

因为 time 服务端发送的是二进制数据,不便直接阅读,我们编写一个客户端来解析并打印收到的 4 个字节数据。这个程序只需要关注“三个半事件”中的“消息/数据到达”事件,事件处理函数如下:

   1: void TimeClient::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
   2: {
   3:   if (buf->readableBytes() >= sizeof(int32_t))
   4:   {
   5:     const void* data = buf->peek();
   6:     int32_t time = *static_cast<const int32_t*>(data);
   7:     buf->retrieve(sizeof(int32_t));
   8:     time_t servertime = sockets::networkToHost32(time);
   9:     Timestamp t(servertime * Timestamp::kMicroSecondsPerSecond);
  10:     LOG_INFO << "Server time = " << servertime << ", " << t.toFormattedString();
  11:   }
  12:   else
  13:   {
  14:     LOG_INFO << conn->name() << " no enough data " << buf->readableBytes()
  15:      << " at " << receiveTime.toFormattedString();
  16:   }
  17: }

注意其中考虑到了如果数据没有一次性收全,已经收到的数据会暂存在 Buffer 里,以等待下一次机会,程序也不会阻塞。这样即便服务器一个字节一个字节地发送数据,代码还是能正常工作,这也是非阻塞网络编程必须在用户态使用接受缓冲的主要原因。

这是我们第一次用到 TcpClient class,完整的代码如下:

   1: #include 
   2: #include 
   3: #include 
   4: #include 
   5: #include 
   6:  
   7: #include 
   8:  
   9: #include 
  10:  
  11: #include 
  12: #include 
  13:  
  14: using namespace muduo;
  15: using namespace muduo::net;
  16:  
  17: class TimeClient : boost::noncopyable
  18: {
  19:  public:
  20:   TimeClient(EventLoop* loop, const InetAddress& listenAddr)
  21:     : loop_(loop),
  22:       client_(loop, listenAddr, "TimeClient")
  23:   {
  24:     client_.setConnectionCallback(
  25:         boost::bind(&TimeClient::onConnection, this, _1));
  26:     client_.setMessageCallback(
  27:         boost::bind(&TimeClient::onMessage, this, _1, _2, _3));
  28:     // client_.enableRetry();
  29:   }
  30:  
  31:   void connect()
  32:   {
  33:     client_.connect();
  34:   }
  35:  
  36:  private:
  37:   void onConnection(const TcpConnectionPtr& conn)
  38:   {
  39:     LOG_INFO << conn->localAddress().toHostPort() << " -> "
  40:         << conn->peerAddress().toHostPort() << " is "
  41:         << (conn->connected() ? "UP" : "DOWN");
  42:  
  43:     if (!conn->connected())  // 如果连接断开,则终止主循环,退出程序
  44:       loop_->quit();
  45:   }
  46:  
  47:   void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
  48:   {
  49:     if (buf->readableBytes() >= sizeof(int32_t))
  50:     {
  51:       const void* data = buf->peek();
  52:       int32_t time = *static_cast<const int32_t*>(data);
  53:       buf->retrieve(sizeof(int32_t));
  54:       time_t servertime = sockets::networkToHost32(time);
  55:       Timestamp t(servertime * Timestamp::kMicroSecondsPerSecond);
  56:       LOG_INFO << "Server time = " << servertime << ", " << t.toFormattedString();
  57:     }
  58:     else
  59:     {
  60:       LOG_INFO << conn->name() << " no enough data " << buf->readableBytes()
  61:        << " at " << receiveTime.toFormattedString();
  62:     }
  63:   }
  64:  
  65:   EventLoop* loop_;
  66:   TcpClient client_;
  67: };
  68:  
  69: int main(int argc, char* argv[])
  70: {
  71:   LOG_INFO << "pid = " << getpid();
  72:   if (argc > 1)
  73:   {
  74:     EventLoop loop;
  75:     InetAddress serverAddr(argv[1], 2037);
  76:  
  77:     TimeClient timeClient(&loop, serverAddr);
  78:     timeClient.connect();
  79:     loop.loop();
  80:   }
  81:   else
  82:   {
  83:     printf("Usage: %s host_ip/n", argv[0]);
  84:   }
  85: }

程序的运行结果如下,假设 time server 运行在本机:

$ ./simple_timeclient 127.0.0.1
2011-02-02 04:10:35.181717  4296 INFO pid = 4296 - timeclient.cc:71
2011-02-02 04:10:35.183668  4296 INFO TcpClient::connect[TimeClient] - connecting to 127.0.0.1:2037 - TcpClient.cc:60
2011-02-02 04:10:35.185178  4296 INFO 127.0.0.1:40960 -> 127.0.0.1:2037 is UP - timeclient.cc:39
2011-02-02 04:10:35.185279  4296 INFO Server time = 1296619835, 2011-02-02 04:10:35.000000 - timeclient.cc:56
2011-02-02 04:10:35.185354  4296 INFO 127.0.0.1:40960 -> 127.0.0.1:2037 is DOWN - timeclient.cc:39

 

echo

Echo 是我们遇到的第一个带交互的协议:服务端把客户端发过来的数据原封不动地传回去。它只需要关注“三个半事件”中的“消息/数据到达”事件,事件处理函数如下:

   1: void EchoServer::onMessage(const TcpConnectionPtr& conn,
   2:                            Buffer* buf,
   3:                            Timestamp time)
   4: {
   5:   string msg(buf->retrieveAsString());
   6:   LOG_INFO << conn->name() << " echo " << msg.size() << " bytes at " << time.toString();
   7:   conn->send(msg);
   8: }

这段代码实现的不是行回显(line echo)服务,而是有一点数据就发送一点数据。这样可以避免客户端恶意地不发送换行字符,而服务端又必须缓存已经收到的数据,导致服务器内存暴涨。但这个程序还是有一个安全漏洞,即如果客户端故意不断发生数据,但从不接收,那么服务端的发送缓冲区会一直堆积,导致内存暴涨。解决办法可以参考下面的 chargen 协议。

剩下的都是例行公事的代码,为节省篇幅,此处从略,请阅读 muduo/examples/simple/echo。

练习 1:修改 EchoServer::onMessage(),实现大小写互换。

练习 2:修改 EchoServer::onMessage(),实现 rot13 加密。

chargen

Chargen 协议很特殊,它只发送数据,不接收数据。而且,它发送数据的速度不能快过客户端接收的速度,因此需要关注“三个半事件”中的半个“消息/数据发送完毕”事件(onWriteComplete),事件处理函数如下:

   1: void ChargenServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
   2: {
   3:   LOG_INFO << "ChargenServer - " << conn->peerAddress().toHostPort() << " -> "
   4:     << conn->localAddress().toHostPort() << " is "
   5:     << (conn->connected() ? "UP" : "DOWN");
   6:   if (conn->connected())
   7:   {
   8:     conn->send(message_);  // 在连接建立时发生第一次数据
   9:   }
  10: }
  11:  
  12: void ChargenServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
  13:                  muduo::net::Buffer* buf,
  14:                  muduo::Timestamp time)
  15: {
  16:   string msg(buf->retrieveAsString());
  17:   LOG_INFO << conn->name() << " discards " << msg.size() << " bytes at " << time.toString();
  18: }
  19:  
  20: void ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)
  21: {
  22:   transferred_ += message_.size();
  23:   conn->send(message_);  // 继续发送数据
  24: }

剩下的都是例行公事的代码,为节省篇幅,此处从略,请阅读 muduo/examples/simple/chargen。

完整的 chargen 服务端还带流量统计功能,用到了定时器,我们会在下一篇文章里介绍定时器的使用,到时候再回头来看相关代码。

用 netcat 扮演客户端,运行结果如下:

$ nc localhost 2019 | head
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefgh
"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghi
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghij
$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijk
%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijkl
&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklm
'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmn
()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmno
)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnop
*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnopq

Five in one

前面五个程序都用到了 EventLoop,这其实是个 Reactor,用于注册和分发 IO 事件。Muduo 遵循 one loop per thread 模型,多个服务端(TcpServer)和客户端(TcpClient)可以共享同一个 EventLoop,也可以分配到多个 EventLoop 上以发挥多核多线程的好处。这里我们把五个服务端用同一个 EventLoop 跑起来,程序还是单线程的,功能却强大了很多:

   1: #include "../chargen/chargen.h"
   2: #include "../daytime/daytime.h"
   3: #include "../discard/discard.h"
   4: #include "../echo/echo.h"
   5: #include "../time/time.h"
   6:  
   7: #include 
   8: #include 
   9:  
  10: #include 
  11:  
  12: using namespace muduo;
  13: using namespace muduo::net;
  14:  
  15: int main()
  16: {
  17:   LOG_INFO << "pid = " << getpid();
  18:   EventLoop loop;
  19:  
  20:   ChargenServer ChargenServer(&loop, InetAddress(2019));
  21:   ChargenServer.start();
  22:  
  23:   DaytimeServer daytimeServer(&loop, InetAddress(2013));
  24:   daytimeServer.start();
  25:  
  26:   DiscardServer discardServer(&loop, InetAddress(2009));
  27:   discardServer.start();
  28:  
  29:   EchoServer echoServer(&loop, InetAddress(2007));
  30:   echoServer.start();
  31:  
  32:   TimeServer timeServer(&loop, InetAddress(2037));
  33:   timeServer.start();
  34:  
  35:   loop.loop();
  36: }

 

以上几个协议的消息格式都非常简单,没有涉及 TCP 网络编程中常见的分包处理,在下一篇文章讲 Boost.Asio 的聊天服务器时我们再来讨论这个问题。

(待续)

Muduo 网络编程示例之二:Boost.Asio 的聊天服务器 这是《Muduo 网络编程示例》系列的第二篇文章。 本文讲介绍一个与 Boost.Asio 的示例代码中的聊天服务器功能类似的网络服务程序,包括客户端与服务端的 muduo 实现。这个例子的主要目的是介绍如何处理分包,并初步涉及 Muduo 的多线程功能。 阅读详情

相关推荐

muduo---C++网络编程库

陈硕 muduo 参考资料 muduo源码剖析 muduo基于Reactor模式实现 Reactor模式也是目前大多数Linux端高性能网络编程框架和网络应用所选择的主要架构,例如内存数据库Redis和Java的Netty库等。 muduo 架构 整个架构依照reactor模式,如如下图: 所谓的reactor模型,是有一个循环的过程,监听对应事件是否触发,触发时调用对应的callback函数进行处理; 这里的事件在muduo中包括socket可读写事件、定时器事件。在其他网络库中如libevent也包

weixin_48433164的博客 3941

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第七章 muduo编程示例(上)

本章将介绍如何用muduo网络库完成常见的TCP网络编程任务。内容如下: 1.[UNP]中的五个简单协议,包括echo、daytime、time、discard、chargen等。 2.文件传输,示范非阻塞TCP网络程序中如何完整地发送数据。 3.Boost.Asio中的示例,包括timer2~6、chat等。chat实现了TCP封包与拆包(codec,coder-decoder的简称,编解码器)。 4.muduo Buffer class的设计与使用。 5.Protobuf(Protobuf(Protoc

tus00000的博客 661

ceres-solver-1.14.0.tar.gz 安装包(官网下载)适合于Linux

ceres-solver-1.14.0.tar.gz 安装包

依赖库:Ceres-solver-2.0.0安装

Ceres Solver是谷歌开源的C++非线性优化库,能够解决有约束或无约束条件下的非线性最小二乘问题。2010年之后大量的运用在谷歌的产品开发中,尤其在谷歌开源的cartographer中被大量的使用。ceres可以在Linux,Windows,macOS,Andrioid,IOS系统进行安装使用ceres被命名是由于高斯使用了最小二乘方法成功的预测了绕行至太阳背后的小行星ceres的位置。本篇博客在 ubuntu 20.04 环境下安装 Ceres-solver-2.0.0。

月照银海似蛟龙的博客 5454

RKDevTool解包和打包(Windows)

我们将修改后的打包工具存放在了RKDevTool_Release_v2.92软件压缩包的rockdev目录下,可以搭配RKDevTool的解包功能使用。双击板卡主芯片对应的bat文件,开始进行打包。红框中的内容是我们的打包工具及辅助打包的可执行文件,Image目录下存放了我们的分区镜像, package-file是分区与分区镜像文件的对应关系。将从网盘获取的Update.zip镜像解压成.img格式并重命名为update.img,也可以直接使用SDK生成的update.img镜像。

tianpu2320959696的博客 3348

通俗易通解释SLAM问题的数学描述:运动方程和观测方程

高翔博士在他的《视觉SLAM十四讲》中把SLAM问题的数学描述抽象成了两个数学方程,以下对此做个总结,并且对此做一些通俗易懂的解释: 一、方程的总体形式 作为理性层次的描述,我们引入数学方程来描述SLAM过程,其中第一个方程叫做运动方程、第二个方程叫做观测方程,想象一下我们在研究SLAM的时候肯定需要观测机器人(无人汽车、无人机等都可以统称为机器人)的运动(画面感很强哈哈),这样很容易理解并且记忆这两个方程的名称和意义: 二、变量的含义 我们看到,这两个方程中有大量的英文字母作为变量存在其中,下面介绍一下

weixin_41855010的博客 7108

muduo编程示例之五个简单TCP示例

文章目录前言五个简单TCP1. discard2.daytime3. echo4. time5. chargen 前言 五个简单TCP示例,分别是echo,discard, 插入跟, daytime, time。简介如下: discard: 丢弃所有收到的数据。 daytime:服务端accept连接之后,以字符串形式发送当前时间,然后主动断开连接。 time:服务端accept连接之后,以二进制形式发送当前时间,然后主动断开连接,我们需要一个客户程序来把收到的时间转换为字符串。 echo:回显服务,把收到

瞧,那小子真帅 637

自托管AI邮件助手imap-mcp:安全连接Claude与个人邮箱的完整指南

在AI助手日益普及的今天,如何安全地让AI访问个人邮箱数据成为关键挑战。传统方法如直接暴露IMAP/SMTP凭证存在严重安全隐患,而第三方SaaS服务则涉及隐私泄露风险。Model Context Protocol (MCP) 作为一种新兴的标准化协议,为AI工具安全接入提供了解决方案。其核心价值在于实现数据源与AI助手间的安全、标准化交互,特别适用于处理敏感信息的场景。imap-mcp正是基于MCP协议构建的自托管邮件网关,它通过多层安全机制(包括主密钥加密和OAuth令牌哈希化)保护用户凭证,让用户能在

weixin_28295799的博客 653

Ubuntu18.04(wsl)编译ceres-2.0.0版本

使用的系统版本是wsl-Ubuntu18.04版本,在编译的过程中,其出现了许多在双系统上面没有出现的问题,花费了较多的时间进行解决。

GFCLJY的博客 905

低成本主从机械臂系统构建:6D位姿数据采集与VLA模型训练实战

本文详细介绍了如何构建低成本主从机械臂系统,实现高精度6D位姿数据采集与VLA模型训练。通过300美元级别的硬件组合,系统达到±1mm位置精度和≤0.5°姿态误差,满足Vision-Language-Action模型的多模态训练需求。文章涵盖硬件选型、数据采集技巧、模型训练实战经验,为机器人研究提供经济高效的解决方案。

r7s8t的博客 262

Labview数据存储与读取——TDMS文件的创建与写入

Labview数据存储与读取——TDMS文件的创建与写入 你好,这是我在自学Labview编写软件过程中使用的一些功能。我在存储采集卡数据时,通过阅读大量他人的程序,发现TDM及TDMS文件十分适合波形数据的记录,TDMS文件比TDM文件在存储动态类型数据时读写速度更快,而且TDMS 文件没有容量限制,适合存储数据量庞大的测试数据。 前言少叙,书归正传 这一篇博客,说明一下如何使用基本的TDMS功能实现数据文件的存储。 所需要的控件如上图所示,在程序框图——编程——文件I\O——TDMS 中,写入TDMS

滑板小帅程序开发 2万+

第7章 muduo编程示例(1)

本章将介绍如何用muduo网络库完成常见的TCP网络编程任务。内容如下: 1.[UNP]中的五个简单协议。2.文件传输,示范非阻塞TCP网络程序中如何完整地发送数据。3.Boost.Asio中的示例。4.muduo Buffer class的设计与使用。5.Protobuf编码解码器(codec)与消息分发器(dispatcher)。6.限制服务器的最大并发连接数。 7.Java Netty中的示例。8.用于测试两台机器的往返延迟的roundtrip。9.用timing wheel踢掉空闲连接。等等。。。

chenyijun的专栏 1148

ROS机器人未知环境自主探索功能包explore_lite最全源码详细解析(二)

本系列文章主要针对ROS机器人常使用的未知环境自主探索功能包explore_lite展开全源码的详细解析,并进行概括总结。 本系列文章共包含六篇文章,前五篇文章主要介绍explore_lite功能包中 explore.cpp、costmap_tools.h、frontier_search.cpp、costmap_client.cpp等源码实现文件中全部函数的详细介绍,第六篇文章进行概括总结,包含自主探索功能包explore_lite的简介,实现未知环境自主探索功能的原理及流程总结,效果演示,使用功能包exp

慕羽★的博客 1647

Muduo 网络编程示例之九:简单的消息广播服务

本文介绍用 muduo 实现一个简单的 topic-based 消息广播服务,这其实是“聊天室”的一个简单扩展,不过聊天的不是人,而是分布式系统中的程序。

陈硕的Blog 2万+

智能车图像处理0-变量释义

这篇文章主要讲述智能车图像处理中,各篇主要变量的含义。如果在看具体分析代码时感到困惑,可以在本篇文章中查询,辅助理解。

taiyuezyh的博客 3666

YOLOv11改进 | 融合改进篇 | 利用尺度统一检测头DynamicHead融合P2增加小目标检测层(让小目标无所遁形)

本文给大家带来的最新改进机制是针对性的改进,针对于小目标检测增加P2层针对于大目标检测增加P6层利用DynamicHead(原版本一比一复现,全网独一份,不同于网上魔改版本)进行检测,其中我们增加P2层其拥有更高的分辨率,这使得模型能够更好地捕捉到小尺寸目标的细节。我们增加P6层是一个较低分辨率但具有更大感受野的特征层。对于大尺寸目标,这意味着模型可以更有效地捕捉到整体的结构信息。在这些的基础上我们配合DynamicHead可以使模型根据不同尺寸的目标动态调整其检测策略,进一步提升模型的精度。

Snu77的博客 3940

TCP网络编程库——Muduo库

Muduo库的运用

m0_74246469的博客 2101

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Muduo 网络编程示例之五: 测量两台机器的网络延迟

Muduo 网络编程示例之五: 测量两台机器的网络延迟 陈硕 (giantchen_AT_gmail) Blog.youkuaiyun.com/Solstice  t.sina.com.cn/giantchen 这是《Muduo 网络编程示例》系列的第五篇文章。 Muduo 全系列文章列表: http://blog.youkuaiyun.com/Solstice/category/779646.aspx   本文介绍一个简单的网络程序 roundtrip,用于测量两台机器之间的网络延迟,即“往返时间 / round t

陈硕的Blog 1万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

muduo学习笔记:net部分之实现TCP网络编程库-Buffer

non-blocking IO的核心思想是避免阻塞在read()或write()或其他IO系统调用上,这样可以最大限度地复用thread-of-control。让一个线程能服务于多个socket连接。IO线程只能阻塞在IO multiplexing函数上,如select/poll/epoll_wait。这样一来,应用层的缓冲是必需的,每个TCP socket都要有stateful的input buffer和output buffer。 ...

热爱生活,忠于自己 2074

阻塞/非阻塞读写总结、tcp网络编程的本质、muduo::Buffer设计简介

一、阻塞/非阻塞读写总结 1、对于read 调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会返回 20;对于 write调用,如果请求写 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会阻塞,直到把 100个字节全部交给发送缓冲区才返回。但如果 socket文件描述符有 O_NONBLOCK标志,则 write不阻塞,直接返回 20;此时非

Meditation 1万+
上一篇: Muduo 网络编程示例之零:前言
下一篇: Muduo 网络编程示例之二:Boost.Asio 的聊天服务器
陈硕
博客等级 码龄26年 1万+粉丝 144原创
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值