最近几天在学习asio异步网络库,今天整理下前几日学习asio库同步读写demo的案例,明天我会整理asio配置的过程。
之前也学习过winsocket,但是不论是winsock还是asio都经历这样几个阶段:
服务端:
- socket创建socket套接字。
- bind绑定本机ip+port。
- listen监听,若在监听到请求,就建立起连接。
- accept 相当于接起电话。
- read、write拿起电话就可以开始说话了。
对于客户端是这样的
客户端:
- socket创建socket套接字。
- connect根据服务端ip+port,发起连接请求。
- write、read对面接起电话就可以说话了。
以下就是echo形式的同步读写demo
流程图
我们不难发现,这种echo类型的服务器虽然简单但是存在很多问题:
- 服务器一直在等待客户端发送消息,一直未收到消息就一直阻塞的,导致了服务器的效率很低下。
- 一问一答无法满足实际生活全双工的需求。
- 为了支持多个客户端,因此服务端开辟线程处理请求,但是一个系统开辟的线程是有限的且效率也会随着线程的数目增加而降低
因此这种服务器只是一个小玩具,不要有过多的依赖
客户端
#include<boost/asio.hpp>
#include <iostream>
using namespace std;
using namespace boost::asio::ip;
//定义消息最大长度
const int MAX_LENGTH = 1024;
int main()
{
try {
//上下文
boost::asio::io_context ioc;
//创建断点
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);
//第二个参数是ConstBufferSequence类型,可以理解为一个数组存储很多块地址的首地址
//每个地址对应的内存卡块都是连续的内存空间,这样可以存储更多的数据
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();
}
return 0;
}
服务端
#include <iostream>
#include<boost/asio.hpp>
#include<set>
#include<memory>
using boost::asio::ip::tcp;
using namespace std;
const int MAX_LENGTH = 1024;
typedef std::shared_ptr<tcp::socket> socket_ptr;
std::set<std::shared_ptr<std::thread>> thread_set;
//服务器处理读和写
void session(socket_ptr sock) {
try {
for (;;) {
char data[MAX_LENGTH];
memset(data, '\0', MAX_LENGTH);
boost::system::error_code error;
//服务器一直再等待缓冲区满
//size_t length = boost::asio::read(sock, boost::asio::buffer(data, MAX_LENGTH), 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 (exception& e) {
std::cerr << "Exception in thread:" << e.what() << "\n" << std::endl;
}
}
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);
//创建线程,session跑在独立的线程,是socket要做的工作,socket是工作人员,防止阻塞 但是占用线程并发量很低
auto t = std::make_shared<std::thread>(session, socket);
//防止线程还没跑完for循环结束,所以把智能指针储存起来计数
thread_set.insert(t);
}
}
int main()
{
//主线程等待子线程退出再退出
try {
boost::asio::io_context ioc;
server(ioc, 10086);
for (auto& t : thread_set) {
//调用 join() 会阻塞当前线程,直到 t 指向的线程完成执行。
t->join();
}
}
catch (std::exception& e) {
std::cerr << "Exception:" << e.what() << endl;
}
}