同步读写的客户端和服务器示例

简介

前面我们介绍了boost::asio同步读写的api函数,现在将前面的api串联起来,做一个能跑起来的客户端和服务器。
客户端和服务器采用阻塞的同步读写方式完成通信

客户端设计

客户端设计基本思路是根据服务器对端的ip和端口创建一个endpoint,然后创建socket连接这个endpoint,之后就可以用同步读写的方式发送和接收数据了。

  1. #include <iostream>
  2. #include <boost/asio.hpp>
  3. using namespace std;
  4. using namespace boost::asio::ip;
  5. const int MAX_LENGTH = 1024;
  6. int main()
  7. {
  8. try {
  9. //创建上下文服务
  10. boost::asio::io_context ioc;
  11. //构造endpoint
  12. tcp::endpoint remote_ep(address::from_string("127.0.0.1"), 10086);
  13. tcp::socket sock(ioc);
  14. boost::system::error_code error = boost::asio::error::host_not_found; ;
  15. sock.connect(remote_ep, error);
  16. if (error) {
  17. cout << "connect failed, code is " << error.value() << " error msg is " << error.message();
  18. return 0;
  19. }
  20. std::cout << "Enter message: ";
  21. char request[MAX_LENGTH];
  22. std::cin.getline(request, MAX_LENGTH);
  23. size_t request_length = strlen(request);
  24. boost::asio::write(sock, boost::asio::buffer(request, request_length));
  25. char reply[MAX_LENGTH];
  26. size_t reply_length = boost::asio::read(sock,
  27. boost::asio::buffer(reply, request_length));
  28. std::cout << "Reply is: ";
  29. std::cout.write(reply, reply_length);
  30. std::cout << "\n";
  31. }
  32. catch (std::exception& e) {
  33. std::cerr << "Exception: " << e.what() << endl;
  34. }
  35. return 0;
  36. }

服务器

session函数

创建session函数,该函数为服务器处理客户端请求,每当我们获取客户端连接后就调用该函数。在session函数里里进行echo方式的读写,所谓echo就是应答式的处理

  1. void session(socket_ptr sock) {
  2. try {
  3. for (;;) {
  4. char data[max_length];
  5. memset(data, '\0', max_length);
  6. boost::system::error_code error;
  7. size_t length = sock->read_some(boost::asio::buffer(data, max_length), error);
  8. if (error == boost::asio::error::eof) {
  9. std::cout << "connection closed by peer" << endl;
  10. break;
  11. }
  12. else if (error) {
  13. throw boost::system::system_error(error);
  14. }
  15. cout << "receive from " << sock->remote_endpoint().address().to_string() << endl;
  16. cout << "receive message is " << data << endl;
  17. //回传信息值
  18. boost::asio::write(*sock, boost::asio::buffer(data, length));
  19. }
  20. }
  21. catch (std::exception& e) {
  22. std::cerr << "Exception in thread: " << e.what() << "\n" << std::endl;
  23. }
  24. }

server函数

server函数根据服务器ip和端口创建服务器acceptor用来接收数据,用socket接收新的连接,然后为这个socket创建session。

  1. void server(boost::asio::io_context& io_context, unsigned short port) {
  2. tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
  3. for (;;) {
  4. socket_ptr socket(new tcp::socket(io_context));
  5. a.accept(*socket);
  6. auto t = std::make_shared<std::thread>(session, socket);
  7. thread_set.insert(t);
  8. }
  9. }

创建线程调用session函数可以分配独立的线程用于socket的读写,保证acceptor不会因为socket的读写而阻塞。

同步读写的优劣

1 同步读写的缺陷在于读写是阻塞的,如果客户端对端不发送数据服务器的read操作是阻塞的,这将导致服务器处于阻塞等待状态。
2 可以通过开辟新的线程为新生成的连接处理读写,但是一个进程开辟的线程是有限的,约为2048个线程,在Linux环境可以通过unlimit增加一个进程开辟的线程数,但是线程过多也会导致切换消耗的时间片较多。
3 该服务器和客户端为应答式,实际场景为全双工通信模式,发送和接收要独立分开。
4 该服务器和客户端未考虑粘包处理。
综上所述,是我们这个服务器和客户端存在的问题,为解决上述问题,我们在接下里的文章里做不断完善和改进,主要以异步读写改进上述方案。
当然同步读写的方式也有其优点,比如客户端连接数不多,而且服务器并发性不高的场景,可以使用同步读写的方式。使用同步读写能简化编码难度。

源码连接

https://gitee.com/secondtonone1/boostasio-learn

热门评论

热门文章

  1. Linux环境搭建和编码

    喜欢(594) 浏览(13588)
  2. Qt环境搭建

    喜欢(517) 浏览(24800)
  3. vscode搭建windows C++开发环境

    喜欢(596) 浏览(83748)
  4. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(6108)
  5. 使用hexo搭建个人博客

    喜欢(533) 浏览(11929)

最新评论

  1. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  2. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  3. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  4. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  5. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  6. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  7. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  8. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  9. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  10. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  11. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  12. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  13. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  14. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  15. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  16. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  17. 类和对象 陈宇航:支持!!!!
  18. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  19. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  20. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  21. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  22. 创建项目和编译 secondtonone1:谢谢支持
  23. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好

个人公众号

个人微信