自我介绍
我是一个网安准大三学生,接触过c,go,python,cpp,js等语言,这个暑假主要用的是cpp
使用cpp的情况
我主要调用的第三方库只有 boost 的 asio,beast,json ,用的是 cpp23 ,所以用 co_await 可以写像 js 的 async - await 一样的写法,比较方便
cpp例子
写了几个玩具项目(都是没有readme的,不对外推广)
- https://github.com/0031400/cf-speedtest cpp 版本的 cf 优选 ip
- https://github.com/0031400/songbox cpp 版本的 singbox ,实现了 tun mixed 入栈和 vless websocket 出栈,简易路由,自用完全替代 singbox
- https://github.com/0031400/cppbox 粗糙版本的 songbox ,由于架构糟糕,换成 songbox
- https://github.com/0031400/playa cpp qt 实现 scrcpy 客户端,想实现电脑控制玩手机的鸣潮(笔记本电脑带不动,而且没存储了,手机玩起来挺流畅的,但是屏幕小)
- https://github.com/0031400/tga cpp qt 实现简陋的 telegram 客户端,糟糕透了,qt掌握的本来不全面,tdlib得到的数据都是通过 td_receive 获得,我只能放到子线程,ui里面调用 td_send,然后等待子线程返回结果,筛选可能接收的ui组件,在各个组件之间互相传递 json 内容,成为了史山
- https://github.com/0031400/td-client cpp 版本调用 tdlib 的查看文本聊天记录的 telegram 简陋客户端,比 tga 好的是使用了跨线程的队列加上信号实现了
Messages mesages=co_await client.getMessage();的效果,但是这个在命令行里面可以卡住,如果要搬到qt里面就要改成qt 的信号机制,总不能点击之后UI卡死吧,我还想不到怎么写
对 cpp的感想
总体来说,写 cpp 比 python,go 要麻烦很多
比如 http 请求
用asio 需要建立 socket,然后http::read http::write 才能得到数据,如果是 https 还要加上一堆 ssl 的东西
如果是 go 直接 http.get()
一个简单的测试 https 连接到得到响应状态吗就要写这么多代码
asio::awaitable<void> App::httping(ip::address_v4 address, int &time) {
auto timeout = std::make_shared<bool>(false);
try {
auto endpoint = tcp::endpoint(address, 443);
ssl::context ctx(ssl::context::tls_client);
auto stream =
std::make_shared<beast::ssl_stream<beast::tcp_stream>>(io_, ctx);
beast::get_lowest_layer(*stream).socket().open(tcp::v4());
std::string error_text;
if (!Outbound::bind_interface(
beast::get_lowest_layer(*stream).socket().native_handle(),
error_text)) {
throw std::runtime_error(error_text);
}
auto timer = std::make_shared<asio::steady_timer>(io_);
timer->expires_after(std::chrono::milliseconds(http_time_));
asio::co_spawn(
io_,
[timer, timeout, stream]() -> asio::awaitable<void> {
co_await timer->async_wait(asio::use_awaitable);
*timeout = true;
beast::get_lowest_layer(*stream).socket().close();
},
asio::detached);
auto start = std::chrono::steady_clock::now();
co_await beast::get_lowest_layer(*stream).async_connect(
endpoint, asio::use_awaitable);
if (!SSL_set_tlsext_host_name(stream->native_handle(),
http_host_.c_str())) {
throw std::runtime_error("set ssl ext fail");
}
co_await stream->async_handshake(ssl::stream_base::client,
asio::use_awaitable);
http::request<http::string_body> req{http::verb::get, http_path_, 11};
req.set(http::field::host, http_host_);
req.prepare_payload();
co_await http::async_write(*stream, req, asio::use_awaitable);
beast::flat_buffer buffer;
http::response_parser<http::empty_body> parser;
parser.skip(true);
co_await http::async_read_header(*stream, buffer, parser,
asio::use_awaitable);
auto end = std::chrono::steady_clock::now();
auto status = parser.get().result_int();
beast::get_lowest_layer(*stream).socket().close();
auto cost =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (status != http_code_) {
throw std::runtime_error(std::format("error code: {}", status));
}
time = static_cast<int>(cost.count());
co_return;
} catch (const boost::system::system_error &ec) {
if (*timeout) {
throw std::runtime_error("httping time out");
} else {
throw ec;
}
}
}
但是 go 版本的 cfst 只需要 (除去了匹配地区码的部分,它里面还有重试)
func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration, string) {
hc := http.Client{
Timeout: time.Second * 2,
Transport: &http.Transport{
DialContext: getDialContext(ip),
//TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过证书验证
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // 阻止重定向
},
}
defer hc.CloseIdleConnections()
// 循环测速计算延迟
success := 0
var delay time.Duration
for i := 0; i < PingTimes; i++ {
request, err := http.NewRequest(http.MethodHead, URL, nil)
if err != nil {
log.Fatal("意外的错误,情报告:", err)
return 0, 0, ""
}
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36")
if i == PingTimes-1 {
request.Header.Set("Connection", "close")
}
startTime := time.Now()
response, err := hc.Do(request)
if err != nil {
continue
}
success++
io.Copy(io.Discard, response.Body)
_ = response.Body.Close()
duration := time.Since(startTime)
delay += duration
}
return success, delay, colo
}
要写好多类
比如我的 cf speedtest
解析 cidr 要一个类
统计httping的结果需要一个类
总App运行也要写一个类
写类的时候要 hpp,cpp换来换去
shared_ptr 真的很方便
一个东西要被多个对象调用或者多个线程里面调用都可以用这个来保持分身
auto timer = std::make_shared<asio::steady_timer>(io_);
timer->expires_after(std::chrono::milliseconds(http_time_));
asio::co_spawn(
io_,
[timer, timeout, stream]() -> asio::awaitable<void> {
co_await timer->async_wait(asio::use_awaitable);
*timeout = true;
beast::get_lowest_layer(*stream).socket().close();
},
asio::detached);
比如我这里 timer timeout stream 都用了 shared_ptr ,子线程需要改变外面的变量,如果用引用的话,如果外部先执行完,那么里面的引用就失效了,用裸指针手动 delete 太麻烦了
写 cpp 对变量的作用域掌握的很紧
我不喜欢全局变量,如果是全局的,也要用个 namespace 包裹
写什么逻辑都是先定义一个 class ,在里面写方法,用属性来实现函数之间变量共享
如果是面向过程的go,python不太经常这么写
cpp可以运行的范围比较大
cpp 可以运行在 openwrt,可以和qt很方便结合(安卓没有尝试,因为不会安卓原生)
go 语言对图形化支持没有qt好(不喜欢网页套壳的)
python 也可以用pyqt ,但是运行需要解释器,用虚拟环境要很多存储,比如 openwrt 就不方面运行
cpp轻量
cpp |
go |
|---|
 |
 |
好吧,其实差不多,而且我的逻辑少很多 |
|

但是 gui 也可以这么轻量就非常不错(只是一个很简单的窗口)
总结
虽然 cpp 很强大,但是写 cpp 真的很慢很麻烦