C++ 网络编程-asio
网络编程基本流程
服务端
1)socket——创建socket对象。
2)bind——绑定本机ip+port。
3)listen——监听,若监听到连接,则建立起连接。
4)accept——再创建一个socket对象给客户端收发消息。在现实中服务端面对多个客户端,为了区分各个客户端,则需要为每个客户端都再分配一个socket对象进行收发消息。
5)read、write——收发消息。
客户端
1)socket——创建socket对象。
2)connect——根据服务端ip+port,发起连接请求。
3)write、read——建立连接后,就可发收消息了。
编译Boost库
Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,由Boost社区组织开发、维护。
获取Boost源码 Boost Downloads
在源码文件夹的根目录下有
bootstrap.bat
文件,双击运行会生成b2.exe启动power shell,在根目录执行
“.\b2.exe toolset=msvc”
进行编译编译过后,会在stage文件夹下生成lib文件夹,里面就是Boost的lib库
为VisualStudio配置Boost库
用VisualStudio 创建一个C++控制台应用工程,然后右键工程选择属性
选择VC++目录 -> 在包含目录,添加Boost源码路径;
选择VC++目录 -> 库目录,添加Boost源码路径下的
stage\lib
;
网络编程-同步读写
同步读写指的是在调用read或write
等I/O操作时,当前线程会阻塞
,直到操作完成(即读取到数据或数据被成功发送)。也就是说,在执行读写操作的期间,程序无法进行其他任务,必须等待I/O操作完成后才能继续执行下一步。
当网络延迟较高或需要频繁进行I/O操作时,线程会大量时间处于等待状态,导致CPU资源浪费,降低了系统整体的吞吐量。对于高并发场景,使用同步I/O可能需要大量线程来处理多个连接,这会导致线程切换开销大,系统资源消耗严重。
同步读写-客户端设计
根据服务器对端的ip和端口创建一个endpoint,然后创建socket连接这个endpoint,之后就可以用同步读写的方式发送和接收数据
#include <iostream>
#include <boost/asio.hpp>
using namespace std;
using namespace boost::asio::ip;
const int MAX_LENGTH = 1024;
int main()
{
try {
//创建上下文服务
boost::asio::io_context ioc;
//构造endpoint
tcp::endpoint remote_ep(address::from_string("127.0.0.1"), 10086);
tcp::socket sock(ioc);
boost::system::error_code error = boost::asio::error::host_not_found; ;
sock.connect(remote_ep, error);
if (error) {
cout << "connect failed, code is " << error.value() << " error msg is " << error.message();
return 0;
}
std::cout << "Enter message: ";
char request[MAX_LENGTH];
std::cin.getline(request, MAX_LENGTH);
size_t request_length = strlen(request);
boost::asio::write(sock, boost::asio::buffer(request, request_length));
char reply[MAX_LENGTH];
size_t reply_length = boost::asio::read(sock,
boost::asio::buffer(reply, request_length));
std::cout << "Reply is: ";
std::cout.write(reply, reply_length);
std::cout << "\n";
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << endl;
}
return 0;
}
同步读写-服务器端设计
#include <iostream>
#include <cstdlib>
#include <iostream>
#include <boost/asio.hpp>
#include <set>
using boost::asio::ip::tcp;
const int max_length = 1024;
typedef std::shared_ptr<tcp::socket> socket_ptr;
std::set<std::shared_ptr<std::thread>> thread_set;
using namespace std;
/*
session函数
为服务器处理客户端请求,每当获取客户端连接后就调用该函数。
在session函数里里进行应答式的读写
*/
void session(socket_ptr sock) {
try {
for (;;) {
char data[max_length];
memset(data, '\0', max_length);
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data, max_length), error);
if (error == boost::asio::error::eof) {
std::cout << "connection closed by peer" << endl;
break;
}
else if (error) {
throw boost::system::system_error(error);
}
cout << "receive from " << sock->remote_endpoint().address().to_string() << endl;
cout << "receive message is " << data << endl;
//回传信息值
boost::asio::write(*sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e) {
std::cerr << "Exception in thread: " << e.what() << "\n" << std::endl;
}
}
/*
server
根据服务器ip和端口创建服务器acceptor用来接收数据
用socket接收新的连接,为socket创建session。
创建线程调用session函数可以分配独立的线程用于socket的读写,保证acceptor不会因为socket的读写而阻塞
*/
void server(boost::asio::io_context& io_context, unsigned short port) {
tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
for (;;) {
socket_ptr socket(new tcp::socket(io_context));
a.accept(*socket);
auto t = std::make_shared<std::thread>(session, socket);
thread_set.insert(t);
}
}
int main()
{
try {
boost::asio::io_context ioc;
server(ioc, 10086);
for (auto& t : thread_set) {
t->join();
}
}
catch (std::exception& e) {
std::cerr << "Exception " << e.what() << "\n";
}
return 0;
}
评论区