鱼C论坛

 找回密码
 立即注册
查看: 2855|回复: 0

[技术交流] 《C++Boost库asio》第八篇 Echo服务器

[复制链接]
发表于 2017-7-11 17:49:48 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

  1. // Echo服务器 :
  2. // 客户端给服务器发来的信息,服务器原封不动的还给客户端。作用:保证服务器可以完
  3. // 整的收到客户端的消息包,并且能把客户端的信息完整的会给客户端,确认两边通信是没有问题的
  4. // 消息包:可以打上时间戳。包的长度两边都是不知道的

  5. // 我另外加入了一条逻辑,当有新的客户端进入后,先发送一条当前时间的消息给客户端;
  6. // 再开始原封不动的把客户端发来的消息还给客户端
  7. #include <boost/asio.hpp>
  8. #include <utility>
  9. #include <iostream>
  10. #include <string>

  11. using boost::asio::ip::tcp;

  12. std::string make_daytime_string()
  13. {
  14.         using namespace std;//For time_t, time and ctime;
  15.                                                 //time_t now = time(0);
  16.         auto now = time(nullptr);
  17.         return ctime(&now); // 返回const char*
  18. }

  19. // session是智能指针管理方法,从public std::enable_shared_from_this<session>派生出来就意
  20. // 味着自身是一个智能指针
  21. class session : public std::enable_shared_from_this<session>
  22. {
  23. public:
  24.         session(tcp::socket socket) : socket_(std::move(socket))
  25.         {
  26.                 // start();
  27.                 // do_read();
  28.                 // 如果写了start()或者do_read(),就调用了shared_from_this(),但是这个时候自身构造还没有完毕,还在
  29.                 //构造函数里,没有出构造这个圈子,这个时候调用shared_from_this()是没办法通过自身来构造
  30.                 //shared_from_this的,构造没完成,new就没完成,自己就还不是智能指针呢,所以public
  31.                 //std::enable_shared_from_this<session>的派生类不能在构造函数和析构函数调用
  32.                 //shared_from_this(),这个是它的局限性。
  33.         }

  34.         void start()
  35.         {
  36.                 first_msg();
  37.         }

  38. private:
  39.         // 另外加的逻辑,先发送一条消息给客户端;
  40.         void first_msg()
  41.         {
  42.                 message_ = make_daytime_string();//返回当前时间

  43.                 auto self = shared_from_this();
  44.                 boost::asio::async_write(
  45.                         socket_,
  46.                         boost::asio::buffer(message_),
  47.                         [self = std::move(self)](const boost::system::error_code & error, std::size_t length){
  48.                         self->do_read(error);
  49.                         std::cout << "send first msg..." << std::endl;
  50.                 });

  51.         }

  52.         void do_read(const boost::system::error_code & error)
  53.         {
  54.                 if (!error) {

  55.                         // 等待客户端信息的传来

  56.                         // 拷贝,引用计数加1,shared_from_this()是右值
  57.                         auto self(shared_from_this());

  58.                         //发出一个事件,收到一点数据也可以,读一些客户端传来的信息,存到data数组里,最
  59.                         //大长度1024,self传入进来的作用是做一个拷贝,拷贝后,它的声明周期和lambda的是一样长的,
  60.                         //引用计数加1
  61.                         socket_.async_read_some(
  62.                                 boost::asio::buffer(data_, max_length),
  63.                                 [this, self](boost::system::error_code ec, std::size_t length)
  64.                         {
  65.                                 if (!ec)
  66.                                 {
  67.                                         do_write(ec, length);//写数据给客户端
  68.                                 }
  69.                         });
  70.                 }
  71.                 else
  72.                 {
  73.                         std::cout << "in do_read error" << std::endl;
  74.                 }
  75.         }

  76.         //收到什么信息就写回去
  77.         void do_write(const boost::system::error_code & error, std::size_t length)
  78.         {
  79.                 if (!error) {
  80.                         auto self(shared_from_this());
  81.                         boost::asio::async_write(
  82.                                 socket_, boost::asio::buffer(data_, length),
  83.                                 [this, self](boost::system::error_code ec, std::size_t length)
  84.                         {
  85.                                 if (!ec)
  86.                                 {
  87.                                         do_read(ec);
  88.                                 }
  89.                         });
  90.                 }
  91.                 else
  92.                 {
  93.                         std::cout << "in do_write error" << std::endl;
  94.                 }
  95.         }

  96.         tcp::socket socket_;
  97.         enum { max_length = 1024 };
  98.         char data_[max_length];
  99.         std::string message_;
  100. };

  101. class server
  102. {
  103. public:
  104.         server(boost::asio::io_service &io_service, short port) :
  105.                 acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
  106.                 socket_(io_service)
  107.         {
  108.                 //do_accept();//在构造函数里把资源构造好就可以了,不要去做事情,做的事情移到外面来
  109.         }

  110.         //提供一个外部函数,没有必要构造好就立即工作,可以在某个时机调用
  111.         void start()
  112.         {
  113.                 do_accept();
  114.         }

  115. private:
  116.         //接收客户端,对客户端进行构造,调用start,接着接收下一个客户端
  117.         void do_accept()  //异步接收客户端的连接
  118.         {

  119.                 acceptor_.async_accept(socket_, [this](boost::system::error_code ec)
  120.                 {
  121.                         if (!ec)
  122.                         {
  123.                                 //move操作过后,socket相当于还是socket_(io_service)这种状态.
  124.                                 auto newSession = std::make_shared<session>(std::move(socket_));
  125.                                 newSession->start();
  126.                         }//session当成一个客户端

  127.                         do_accept();//不管错没错都接着接收客户端
  128.                 });
  129.         }

  130.         tcp::acceptor acceptor_;//监听端口号,连接新的客户端
  131.         tcp::socket socket_;
  132. };

  133. int main()
  134. {
  135.         // 我们的程序和操作系统底层之间的一个相互传递的枢纽
  136.         // 会把事件按照一定方式进行处理;
  137.         boost::asio::io_service io;

  138.         try
  139.         {

  140.                 server s(io, 2001);
  141.                 s.start();

  142.                 io.run();

  143.         }
  144.         catch (std::exception &e)
  145.         {
  146.                 std::cerr << e.what() << std::endl;
  147.         }


  148.         std::cout << "mian finish run \n" << std::endl;// 提示下main函数已经结束了
  149.         system("pause");// 让VS控制台程序不会一闪而过;
  150.         return 0;
  151. }
复制代码




//move完后,原先的值是未定义的状态,相当于是被初始化了。
  1. class ObjectWithMove {
  2. public:
  3.         ObjectWithMove(int a) : m_a(a), m_p(p) {}
  4.         ObjectWithMove(ObjectWithMove&& lhs) : m_a(lhs.m_a), m_p(lhs.m_p) {
  5.                 lhs.m_a = 0;
  6.                 lhs.m_p = nullptr;
  7.         }
  8. private:
  9.         int m_a;
  10.         int *m_p;
  11. };
复制代码


用C#写了个简单的客户端来测试:

  1. using System.Net;
  2. using System.Net.Sockets;
  3. using System.Threading;
  4. using System.Text;
  5. using System;

  6. public class Test002 {
  7.        
  8.         private static byte[] result = new byte[1024];  
  9.        
  10.         int main () {
  11.                
  12.                 //设定服务器IP地址  
  13.                 IPAddress ip = IPAddress.Parse("127.0.0.1");  
  14.                 Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
  15.                 try  
  16.                 {  
  17.                         clientSocket.Connect(new IPEndPoint(ip, 2001)); //配置服务器IP与端口  
  18.                         Console.WriteLine("连接服务器成功");  
  19.                 }  
  20.                 catch  
  21.                 {  
  22.                         Console.WriteLine("连接服务器失败,请按回车键退出!");  
  23.                         return;  
  24.                 }  
  25.                 //通过clientSocket接收数据  
  26.                 int receiveLength = clientSocket.Receive(result);  
  27.                 Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));
  28.                 //通过 clientSocket 发送数据  
  29.                 for (int i = 0; i < 3; i++)  
  30.                 {  
  31.                         try  
  32.                         {  
  33.                                 Thread.Sleep(1000);    //等待1秒钟  
  34.                                 string sendMessage = "client send Message Hellp" + DateTime.Now;  
  35.                                 clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));  
  36.                                 Console.WriteLine("向服务器发送消息:" + sendMessage);  
  37.                                
  38.                                 receiveLength = clientSocket.Receive(result);  
  39.                                 Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));

  40.                         }  
  41.                         catch  
  42.                         {  
  43.                                 Console.WriteLine("与服务器断开连接");  
  44.                                 clientSocket.Shutdown(SocketShutdown.Both);  
  45.                                 clientSocket.Close();  
  46.                                 break;  
  47.                         }  
  48.                 }  
  49.                 Console.WriteLine("发送完毕,按回车键退出");   
  50.                
  51.         }
  52.        
  53. }
复制代码


对应的C++客户端在下一篇研究

评分

参与人数 1鱼币 +4 收起 理由
小甲鱼 + 4 支持楼主!

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-21 01:41

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表