- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章C++编写高性能服务器实例教程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
我将展示如何使用现代C++编写一个Echo服务器,相当于分布式系统开发中的“Hello World”。这个服务器会将接收的消息直接返回。我们同时需要一个可以向我们的服务器发动消息的客户端,在这里可以发现客户端的源码.
Wangle是一个用来搭建事件驱动的现代异步C++服务的C/S应用框架。Wangle最基本的抽象概念就是Pipeline(管线)。能够理解这种抽象,将会很容易写出各种复杂的现代C++服务,另一个重要的概念是Service(服务),其可以看作一种更高级的Pipeline,不过超出了本文我们关注的范畴.
PipeLine 。
pipeline 是 Wangle 中最重要也是最强大的抽象,可以让用户在定制 request 和 response 的实现时拥有很大的自由。一个pipeline就是一系列request/response控制程序的嵌套。我试图寻找一个真实世界中pipeline的类比,唯一我能想到的就是现实世界工厂中的生产线。一条生产线工作在一种顺序模式下,所有的工人取得一个物体,并且只添加一种修改,再将其发送给上游的工人直到整个产品制造完成。这可能不是一个特别好的比喻,因为流水线上产品的流动是单向的,而一个pipeline能控制反方向的数据流动--就好像将成品分解成原材料.
一个Wangle handler可以同时掌控上游和下游的两个方向的数据流动。当你把所有的handler连接在一起,就可以用一种灵活的方式将原始数据组装为想要的数据类型或者将已有的数据拆分.
在我们的服务器的pipeline中大致将会有下面几种handler
1.Handler 1 (下文的上游下游是指对一同个handler而言,根据其在pipeline中的位置不同,输入输出相反) 上游:将从socket中接收的二进制数据流写入一个零拷贝(zero-copy,指省略了Applicaion context和Kernel context之间的上下文切换,避免了CPU对Buffer的冗余拷贝,直接在Kernel级别进行数据传输的技术,详情请参阅维基百科)的字节缓存中,发送给handler2 。
下游:接收一个零拷贝的字节缓存,将其内容写入socket中 。
2.Handler2 上游:接收handler1的缓存对象,解码为一个string对象传递给handler3 下游:接收handler3的string对象,将其转码为一个零拷贝的字节缓存,发送给handler1 。
3.Handler3 上游:接收handler2中的string对象,再向下发送至pipeline等待写回客户端。string会发回handler2 下游:接收上游的string对象,传递给handler2 。
需要注意的一点是,每一个handler应当只做一件事并且只有一件,如果你有一个handler里做了多项任务,比如从二进制流离直接解码出string,那么你需要学会将它拆分。这对提升代码的可维护性和扩展性非常重要.
另外,没错,handler不是线程安全的,所以不要轻易的在其中使用任何没有经过mutex,atomic lock保护的数据,如果你确实需要一个线程安全的环境,Folly提供了一种免于加锁的数据结构, Folly依赖于Wangle,你可以很容易的在项目中引入并使用它.
如果你还不是很明白所有的步骤,不用着急,在看到下面的具体实现时你会更加清楚.
Echo Server 。
下面我会展示服务器的具体实现。我假定您已经安装好Wangle。需要注意的是截至目前Wangle还不能在Mac OS上安装,我建议您可以安装虚拟机,使用Ubuntu来安装Wangle.
这就是echo handler:接收一个string,打印到stdout中,再发送回pipeline。要注意write语句中的定界符不可以省略,因为pipeline会按照字节解码.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// the main logic of our echo server; receives a string and writes it straight
// back
class
EchoHandler :
public
HandlerAdapter {
public
:
virtual
void
read(Context* ctx, std::string msg) override {
std::cout <<
"handling "
<< msg << std::endl;
write(ctx, msg +
"rn"
);
}
};
|
Echohandler其实是我们pipeline的最后一个handler,现在我们需要创建一个PipelineFactory来控制所有的request和response.
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
|
// where we define the chain of handlers for each messeage received
class
EchoPipelineFactory :
public
PipelineFactory {
public
:
EchoPipeline::Ptr newPipeline(std::shared_ptr sock) {
auto pipeline = EchoPipeline::create();
pipeline->addBack(AsyncSocketHandler(sock));
pipeline->addBack(LineBasedFrameDecoder(8192));
pipeline->addBack(StringCodec());
pipeline->addBack(EchoHandler());
pipeline->finalize();
return
pipeline;
}
};
|
pipeline中每一个handler的插入顺序都需要严格注意,因为它们是按照先后排序的,此处我们有4个handler 。
1.AsyncSocketHandler: 上游:读取scoket中的二进制流转换成零拷贝字节缓存 下游:将字节缓存内容写入底层socket 。
2. LineBasedFrameDecoder: 上游:接收字节缓存,按行分割数据 下游:将字节缓存发送给AsyncSocketHandler 。
3. StringCodec: 上游:接收字节缓存,解码为std:string传递给EchoHandler 下游:接收std:string, 编码为字节缓存,传递给LineBasedFrameDecoder 。
4. EchoHandler: 上游:接收std:string对象,将其写入pipeline-将消息返回给Echohandler。 下游:接收一个std:string对象,转发给StringCodec Handler。 现在我们所需要做的就是将pipeline factory关联到ServerBootstrap,绑定一个端口,这样我们已经完成了 基本上所有的工作.
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
#include <gflags/gflags.h>
#include <wangle/bootstrap/ServerBootstrap.h>
#include <wangle/channel/AsyncSocketHandler.h>
#include <wangle/codec/LineBasedFrameDecoder.h>
#include <wangle/codec/StringCodec.h>
using
namespace
folly;
using
namespace
wangle;
DEFINE_int32(port, 8080,
"echo server port"
);
typedef
Pipeline<IOBufQueue&, std::string> EchoPipeline;
// the main logic of our echo server; receives a string and writes it straight
// back
class
EchoHandler :
public
HandlerAdapter<std::string> {
public
:
virtual
void
read(Context* ctx, std::string msg) override {
std::cout <<
"handling "
<< msg << std::endl;
write(ctx, msg +
"\r\n"
);
}
};
// where we define the chain of handlers for each messeage received
class
EchoPipelineFactory :
public
PipelineFactory<EchoPipeline> {
public
:
EchoPipeline::Ptr newPipeline(std::shared_ptr<AsyncTransportWrapper> sock) {
auto pipeline = EchoPipeline::create();
pipeline->addBack(AsyncSocketHandler(sock));
pipeline->addBack(LineBasedFrameDecoder(8192));
pipeline->addBack(StringCodec());
pipeline->addBack(EchoHandler());
pipeline->finalize();
return
pipeline;
}
};
int
main(
int
argc,
char
** argv) {
google::ParseCommandLineFlags(&argc, &argv,
true
);
ServerBootstrap<EchoPipeline> server;
server.childPipeline(std::make_shared<EchoPipelineFactory>());
server.bind(FLAGS_port);
server.waitForStop();
return
0;
}
|
至此我们一共只写了48行代码就完成了一个高性能的异步C++服务器.
Echo Client 。
echo客户端的实现与我们的服务端非常类似
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
|
// the handler for receiving messages back from the server
class
EchoHandler :
public
HandlerAdapter {
public
:
virtual
void
read(Context* ctx, std::string msg) override {
std::cout <<
"received back: "
<< msg;
}
virtual
void
readException(Context* ctx, exception_wrapper e) override {
std::cout << exceptionStr(e) << std::endl;
close(ctx);
}
virtual
void
readEOF(Context* ctx) override {
std::cout <<
"EOF received :("
<< std::endl;
close(ctx);
}
};
|
注意我们重载了readException和readEOF两个方法,还有其他一些方法可以被重载。如果你需要控制某个特别的事件,只需要重载对应的虚函数即可.
这是客户端的pipeline factory的实现,与我们的服务端结构基本一致,只有EventBaseHandler这个handler在服务端代码中不曾出现,它可以确保我们可以从任意一个线程写入数据.
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
|
// the handler for receiving messages back from the server
class
EchoHandler :
public
HandlerAdapter {
public
:
virtual
void
read(Context* ctx, std::string msg) override {
std::cout <<
"received back: "
<< msg;
}
virtual
void
readException(Context* ctx, exception_wrapper e) override {
std::cout << exceptionStr(e) << std::endl;
close(ctx);
}
virtual
void
readEOF(Context* ctx) override {
std::cout <<
"EOF received :("
<< std::endl;
close(ctx);
}
};
|
客户端所有的代码如下图所示 。
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
#include <gflags/gflags.h>
#include
#include <wangle/bootstrap/ClientBootstrap.h>
#include <wangle/channel/AsyncSocketHandler.h>
#include <wangle/channel/EventBaseHandler.h>
#include <wangle/codec/LineBasedFrameDecoder.h>
#include <wangle/codec/StringCodec.h>
using
namespace
folly;
using
namespace
wangle;
DEFINE_int32(port, 8080,
"echo server port"
);
DEFINE_string(host,
"::1"
,
"echo server address"
);
typedef
Pipeline<folly::IOBufQueue&, std::string> EchoPipeline;
// the handler for receiving messages back from the server
class
EchoHandler :
public
HandlerAdapter {
public
:
virtual
void
read(Context* ctx, std::string msg) override {
std::cout <<
"received back: "
<< msg;
}
virtual
void
readException(Context* ctx, exception_wrapper e) override {
std::cout << exceptionStr(e) << std::endl;
close(ctx);
}
virtual
void
readEOF(Context* ctx) override {
std::cout <<
"EOF received :("
<< std::endl;
close(ctx);
}
};
// chains the handlers together to define the response pipeline
class
EchoPipelineFactory :
public
PipelineFactory {
public
:
EchoPipeline::Ptr newPipeline(std::shared_ptr sock) {
auto pipeline = EchoPipeline::create();
pipeline->addBack(AsyncSocketHandler(sock));
pipeline->addBack(
EventBaseHandler());
// ensure we can write from any thread
pipeline->addBack(LineBasedFrameDecoder(8192,
false
));
pipeline->addBack(StringCodec());
pipeline->addBack(EchoHandler());
pipeline->finalize();
return
pipeline;
}
};
int
main(
int
argc,
char
** argv) {
google::ParseCommandLineFlags(&argc, &argv,
true
);
ClientBootstrap client;
client.group(std::make_shared(1));
client.pipelineFactory(std::make_shared());
auto pipeline = client.connect(SocketAddress(FLAGS_host, FLAGS_port)).get();
try
{
while
(
true
) {
std::string line;
std::getline(std::cin, line);
if
(line ==
""
) {
break
;
}
pipeline->write(line +
"rn"
).get();
if
(line ==
"bye"
) {
pipeline->close();
break
;
}
}
}
catch
(
const
std::exception& e) {
std::cout << exceptionStr(e) << std::endl;
}
return
0;
}
|
程序用一个While循环不断监测用户的输入,并且依靠调用.get() 来同步等待一直到请求被响应.
以上就是C++编写高性能服务器实例教程的详细内容,更多关于C++高性能服务器的资料请关注我其它相关文章! 。
原文链接:https://www.imooc.com/article/278409 。
最后此篇关于C++编写高性能服务器实例教程的文章就讲到这里了,如果你想了解更多关于C++编写高性能服务器实例教程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我有以下对象: var myObj = { "4":{//The key is a number String. id:4,name
我需要构建某种字典,其中还包含每个单词在该语言中出现的单词频率。通常,这将使用 std::unordered_map 来实现,对吧?现在问题来了……我想找到所有符合某些正则表达式的单词及其频率,而性能
软件开发通常会提到一个名词 “三高”,即高并发、高性能、高可用。 具体的指标定义,如:高并发方面要求QPS 大于 10万;高性能方面要求请求延迟小于 100 ms;高可用方面要高于 99.99
我有一个大约有 108000 个三角形的网格,应该用 WebGL 渲染。 目前我不使用框架,只使用纯 WebGL。我已经通过 id 映射实现了对象识别,以便使用回调功能和基本的相机操纵器进行拾取。 现
我读过无数的帖子,无数次地重写过代码,我发现自己碰壁了。由于应用了 CSS scale3d 转换的容器,我试图克服在滚动页面时不使用 CSS 固定元素位置的限制。虽然我最新的解决方案有效,但它有时仍然
我正在开发一个嵌入式系统,它需要非常频繁地存储和检索数据。我预计每秒可能有 100 次写入和 10 次读取。数据将以突发而不是连续的方式到达。 我想使用 SQLite 来存储所有这些数据,但由于它是一
当前情况 在我们的一款 SPA Web 应用程序中,我们使用自定义跟踪器请求来跟踪各种用户操作,例如 cta 点击、页面导航 等,该请求将一些信息传递到后端使用 POST 请求。 问题现在,因为这些跟
我正在编写执行大量表查找的程序。因此,当我偶然发现 Data.Map (当然),以及 Data.HashMap 和 Data.Hashtable 时,我正在仔细阅读 Haskell 文档>。我不是散列
我正在评估我们的高性能电信应用程序的 BPEL 引擎,但性能似乎很差。我们评估了 Apache Ode、SunBPEL 引擎、Active BPEL 等。您知道任何更快的 BPEL 引擎实现或 C/C
我目前正在重写一个旧的 ASP\ASP.NET 应用程序。由于我无法完全重写,所以我需要分部分进行。现在我正在将代码库从 ODBC 升级到 .net 连接器。 有一个函数可以在用户每次访问页面时进行一
旁注:我正在使用 JQuery,但没有使用任何第三方表格组件(我现在也不能)。 我的问题是我应该能够显示很多(10000)条经过的消息——每秒都会有数百条新消息,它们应该被添加到底部。当我们达到 10
为了争论,假设我试图在 MySQL 表中表示一个非常简单的文件系统。请注意,这并不是我正在做的,它只是为问题的简单基础做准备。所以不要费心告诉我存储文件的更好方法。该表的架构如下: varchar p
我希望优化我使用python时遇到的大数据解析问题的性能。以防有人感兴趣:下面显示的数据是六种灵长类动物全基因组DNA序列比对的片段。 目前,我知道如何处理这类问题的最好方法是打开我的~250(大小2
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我需要用 Ruby 制作一个小应用程序,它将以不同颜色显示通过 TCP 接收的日志行。推荐用于此类应用的 GUI 框架是什么? 我应该在 Windows 和 Linux 上工作。 最佳答案 Shoes
我正在尝试编写一个非常简单的 Web 服务器来执行以下操作: 接收请求。 响应一个小文件;关闭连接。 处理请求数据。 换句话说,响应不依赖于请求信息,但请求信息仍然很重要。数据将被保留,然后用于分析。
我正在使用 WebSocket 协议(protocol)(通过 WebSocketSharp)开发 C# 游戏服务器。目前,每当用户登录游戏时,我都会创建一个 token 并将其添加到 Concurr
我正在处理应用程序的延迟敏感部分,基本上我会收到一个网络事件来转换数据,然后将所有数据插入到数据库中。在分析之后,我发现基本上我所有的时间都花在了保存数据上。这是代码 private void ins
我正在尝试将视频输出(帧序列)输出到任何 qt 可见小部件。一开始我认为 QLabel 就足够了......但我错了。转换为像素图对于大型图像的处理器来说过于过载:例如 1080p。 还有其他解决方案
我是一名优秀的程序员,十分优秀!