安装
muduo 地址
安装 cmake
1
| sudo apt-get install cmake g++ make
|
安装依赖 boost
1
| sudo apt-get install libboost-dev libboost-test-dev
|
三个非必要依赖库:curl、c-ares DNS、 Google Protobuf。
1
| sudo apt-get install libcurl4-openssl-dev libc-ares-dev protobuf-compiler libprotobuf-dev
|
muduo 编译
1
2
3
4
5
6
7
8
9
10
11
| git clone https://github.com/chenshuo/muduo
cd muduo
./build.sh -j2 #编译 muduo 库和自带的用例,生成可执行文件和静态库分别位于 ../build/debug/{bin,lib}
./build.sh install #将 muduo 头文件和库文件安装到 ../build/debug-install/{include,lib}
## 编译 release 包(以 -O2 优化)
BUILD_TYPE=release ./build.sh -j2 #编译 muduo 库和自带的用例,生成可执行文件和静态库分别位于 ../build/release/{bin,lib}
BUILD_TYPE=release ./build.sh install #将 muduo 头文件和库文件安装到 ../build/release-install/{include,lib},以便 muduo-protorpc 和 muduo-udns 等库使用
|
使用 muduo 库只需要设置好头文件路径(../build/release-install/include)和库文件路径(../build/release-install/lib)并链接相应的静态库文件(-lmuduo_net -lmuduo_base)。
如何 CMake 和 makefile 编译基于 muduo 的程序:https://github.com/chenshuo/muduo-tutorial
目录结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # tree -L 1 muduo
muduo
├── BUILD.bazel # Bazel 构建工具的配置文件
├── CMakeLists.txt # cmake 编译文件
├── ChangeLog # 项目变更日志
├── ChangeLog2 # 项目变更日志
├── License # 项目许可证文件
├── README # 项目说明文档
├── WORKSPACE # Bazel 构建系统的工作区配置文件,用于声明项目的外部依赖(如第三方库),与 BUILD.bazel 配合使用
├── build.sh # 编译脚本
├── contrib # 第三方贡献的扩展模块或工具
├── examples # 示例代码
├── muduo # muduo 主体
└── patches # 补丁文件
5 directories, 8 files
|
基础库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| # tree -L 1 muduo/muduo/base/
muduo/muduo/base/
├── AsyncLogging.cc
├── AsyncLogging.h # 异步日志 backend
├── Atomic.h # 原子操作
├── BUILD.bazel
├── BlockingQueue.h # 无界阻塞队列(生产者消费者队列)
├── BoundedBlockingQueue.h # 有界阻塞队列
├── CMakeLists.txt
├── Condition.cc
├── Condition.h # 条件变量,与 Mutex 一起使用
├── CountDownLatch.cc
├── CountDownLatch.h # “倒计时门闩” 同步
├── CurrentThread.cc
├── CurrentThread.h
├── Date.cc
├── Date.h # julian 日期库(公历)
├── Exception.cc
├── Exception.h # 带 stack trace 的异常基类
├── FileUtil.cc
├── FileUtil.h
├── GzipFile.h
├── LogFile.cc
├── LogFile.h
├── LogStream.cc
├── LogStream.h
├── Logging.cc
├── Logging.h # 简单日志
├── Mutex.h # 互斥锁
├── ProcessInfo.cc
├── ProcessInfo.h # 进程信息
├── Singleton.h # 线程安全的 Singleton
├── StringPiece. # 字符串传递类型
├── Thread.cc
├── Thread.h # 线程对象
├── ThreadLocal.h # 线程局部数据
├── ThreadLocalSingleton.h # 每个线程的 Singleton
├── ThreadPool.cc
├── ThreadPool.h # 简单的固定大小线程池
├── TimeZone.cc
├── TimeZone.h # 时区与夏令时
├── Timestamp.cc
├── Timestamp.h # UTC 时间戳
├── Types.h # 基本类型声明
├── WeakCallback.h
├── copyable.h # 空基类,用于标识值类型
├── noncopyable.h
└── tests # 测试用例
2 directories, 45 files
|
网络核心库
基于 Reactor 模式的网络库,核心是个事件循环 EventLoop,用于响应计时器,和 IO 事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| tree -L 1 muduo/muduo/net
muduo/muduo/net
├── Acceptor.cc
├── Acceptor.h # 接收器,用于服务端网络连接
├── BUILD.bazel
├── Buffer.cc
├── Buffer.h # 缓冲区,非阻塞 IO 必备
├── CMakeLists.txt
├── Callbacks.h
├── Channel.cc
├── Channel.h # 用于每个 socket 连接的事件分发
├── Connector.cc
├── Connector.h # 连接器,用于客户端发起连接
├── Endian.h # 网络字节序与本机字节序转换
├── EventLoop.cc
├── EventLoop.h # 事件分发器
├── EventLoopThread.cc
├── EventLoopThread.h # 专门用于 EventLoop 的线程
├── EventLoopThreadPool.cc
├── EventLoopThreadPool.h # muduo 默认 多线程 IO 模型
├── InetAddress.cc
├── InetAddress.h # Ip 地址的简单封装
├── Poller.cc
├── Poller.h # io multiplexing的基类
├── Socket.cc
├── Socket.h # 封装 socket 描述符,用于关闭连接
├── SocketsOps.cc
├── SocketsOps.h # 封装底层 socket api
├── TcpClient.cc
├── TcpClient.h # Tcp 客户端
├── TcpConnection.cc
├── TcpConnection.h
├── TcpServer.cc
├── TcpServer.h # Tcp 服务端
├── Timer.cc
├── Timer.h
├── TimerId.h
├── TimerQueue.cc
├── TimerQueue.h
├── ZlibStream.h
├── boilerplate.cc
├── boilerplate.h
├── http # 网络附属库 http,需要 -lmuduo_http链接
├── inspect # 网络附属库 窥探进程内部信息,需要 -lmuduo_inspect链接
├── poller # Io multiplexing 实现
├── protobuf
├── protorpc
└── tests # 测试用例
7 directories, 40 files
|
muduo 头文件使用前置声明,减少头文件引入的依赖关系。
https://github.com/chenshuo/muduo-udns :基于 UDNS 的异步 DNS 解析
https://github.com/chenshuo/muduo-protorpc:基于 muduo 的 RPC 框架,自动管理对象生命周期。
TCP 网络编程最本质的是处理三个半事件:
- 连接建立。(客户端发起连接,服务端被动接受连接)
- 消息到达,文件描述符可读。(阻塞和非阻塞,如何处理分包,应用层的缓冲如何设计等)
- 连接断开。(主动断开和被动断开)
3.5. 消息发送完毕(数据写入操作系统的缓冲区,将TCP协议栈负责数据的发送和重传,不代表对方已经收到数据)。
echo 服务实现
muduo 使用不需要指定类派生,不用复写虚函数,只需要注册回调即可。(与 muduo 设计哲学一致)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #ifndef MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H
#define MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H
#include "muduo/net/TcpServer.h"
// RFC 862
class EchoServer
{
public:
EchoServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr);
void start(); // calls server_.start();
private:
void onConnection(const muduo::net::TcpConnectionPtr& conn);
void onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time);
muduo::net::TcpServer server_;
};
#endif // MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| #include "examples/simple/echo/echo.h"
#include "muduo/base/Logging.h"
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
// using namespace muduo;
// using namespace muduo::net;
EchoServer::EchoServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr)
: server_(loop, listenAddr, "EchoServer")
{
server_.setConnectionCallback(
std::bind(&EchoServer::onConnection, this, _1));
server_.setMessageCallback(
std::bind(&EchoServer::onMessage, this, _1, _2, _3));
}
void EchoServer::start()
{
server_.start();
}
void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
}
void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time)
{
muduo::string msg(buf->retrieveAllAsString());
LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
<< "data received at " << time.toString();
conn->send(msg);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #include "examples/simple/echo/echo.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include <unistd.h>
// using namespace muduo;
// using namespace muduo::net;
int main()
{
LOG_INFO << "pid = " << getpid();
muduo::net::EventLoop loop;
muduo::net::InetAddress listenAddr(2007);
EchoServer server(&loop, listenAddr);
server.start();
loop.loop();
}
//nc 127.0.0.1 2007
//telnet 127.0.0.1 2007
|
finger 服务
python twisted 是一个非常好的网络库,采用 reactor 作为网络库基本模型。
- 拒绝连接,空等。
1
2
3
4
5
6
7
8
9
10
| #include "muduo/net/EventLoop.h"
using namespace muduo;
using namespace muduo::net;
int main()
{
EventLoop loop;
loop.loop();
}
|
- 接受新连接,不处理数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
using namespace muduo;
using namespace muduo::net;
int main()
{
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.start();
loop.loop();
}
//nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
- 主动断开连接。通过打印日志发现,使用 nc 客户端链接后,发送数据,服务的接收到第一次发送的数据后直接断开,不在接收数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include "muduo/base/Logging.h"
using namespace muduo;
using namespace muduo::net;
void onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected())
{
LOG_INFO << "Finger - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
conn->shutdown();
}
}
int main()
{
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.setConnectionCallback(onConnection);
server.start();
loop.loop();
}
//printf "msg\r\n" | nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
- 读取用户名,然后断开连接。与 finger03 类似,调用回调不同,一个是连接,一个是数据。
读到 \r\n 结尾的消息就会断开连接。
安全问题:
- 如果客户端不换行会把服务器内存撑爆。
- Buffer::findCRLF()是线性查找的,如果客户端每次发送一个字节,服务的的时间复杂的为 O(n^2),消耗 CPU 资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include "muduo/base/Logging.h"
using namespace muduo;
using namespace muduo::net;
void onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
if (buf->findCRLF())
{
LOG_INFO << "Finger - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
conn->shutdown();
}
}
int main()
{
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
//printf "msg\r\n" | nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
- 读取用户名,输出错我,然后断开连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
using namespace muduo;
using namespace muduo::net;
void onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
if (buf->findCRLF())
{
conn->send("No such user\r\n");
conn->shutdown();
}
}
int main()
{
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
//printf "msg\r\n" | nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
- 在空的 UserMap 中查找用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <map>
using namespace muduo;
using namespace muduo::net;
typedef std::map<string, string> UserMap;
UserMap users;
string getUser(const string& user)
{
string result = "No such user";
UserMap::iterator it = users.find(user);
if (it != users.end())
{
result = it->second;
}
return result;
}
void onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
const char* crlf = buf->findCRLF();
if (crlf)
{
string user(buf->peek(), crlf);
conn->send(getUser(user) + "\r\n");
buf->retrieveUntil(crlf + 2);
conn->shutdown();
}
}
int main()
{
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
//printf "msg\r\n" | nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
- 往 UserMap 中添加用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| #include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <map>
using namespace muduo;
using namespace muduo::net;
typedef std::map<string, string> UserMap;
UserMap users;
string getUser(const string& user)
{
string result = "No such user";
UserMap::iterator it = users.find(user);
if (it != users.end())
{
result = it->second;
}
return result;
}
void onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
const char* crlf = buf->findCRLF();
if (crlf)
{
string user(buf->peek(), crlf);
conn->send(getUser(user) + "\r\n");
buf->retrieveUntil(crlf + 2);
conn->shutdown();
}
}
int main()
{
users["schen"] = "Happy and well";
EventLoop loop;
TcpServer server(&loop, InetAddress(1079), "Finger");
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
//printf "schen\r\n" | nc 127.0.0.1 1079
//telnet 127.0.0.1 1079
|
编译 c-ares 相关问题
https://github.com/cold-rivers-snow/muduo/commit/1a6f10f35a11d36111a041163290b310fb8fbe3f
ARES_VERSION 是 c-ares 库提供的一个预定义宏,在头文件<ares.h>中,它表示当前使用的 c-ares 库的版本号。这个宏的值是一个 24 位的十六进制数
0x010F04 表示 c-ares 1.15.4 版本
0x010500 表示 c-ares 1.5.0 版本
#if ARES_VERSION >= 0x010500 的含义
这行代码是 C/C++ 预处理器的条件编译指令,它的作用是:
“如果当前使用的 c-ares 库版本大于或等于 1.5.0,则编译下面的代码块;否则,跳过该代码块或编译替代代码。”
Reference
http://blog.csdn.net/Solstice/archive/2010/03/10/5364096.aspx
http://www.oschina.net/question/28_61182
http://aur.archlinux.org/packages.php?ID=49251
http://www.cs.nott.ac.uk/~cah/G51ISS/Document/NoSliverBullet.html
http://redmin.lighttpd.net/issues/show/2105
http://download.lighttpd.net/lighttpd/security/lighttpd_sa_2010_01.txt
http://zedshaw.com/essays/programmer_stats.html
http://www.percona.com/files/presentations/VELOCITY2012-Beyond-the-Numbers.pdf
http://gist.github.com/564985
http://think-async.com/Asio/Download
http://monkey.org/~provos/libevent-2.0.6-rc.tar.gz
http://asio.cvs.sourceforge.net/viewvc/asio/asio/src/tests/performance/
http://think-async.com/Asio/LinuxPerformanceImprovements
https://gist.github.com/chenshuo