鱼C论坛

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

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

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

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

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

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

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

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
        using namespace std;//For time_t, time and ctime;
                                                //time_t now = time(0);
        auto now = time(nullptr);
        return ctime(&now); // 返回const char*
}

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

        void start()
        {
                first_msg();
        }

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

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

        }

        void do_read(const boost::system::error_code & error)
        {
                if (!error) {

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

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

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

        //收到什么信息就写回去
        void do_write(const boost::system::error_code & error, std::size_t length)
        {
                if (!error) {
                        auto self(shared_from_this());
                        boost::asio::async_write(
                                socket_, boost::asio::buffer(data_, length),
                                [this, self](boost::system::error_code ec, std::size_t length)
                        {
                                if (!ec)
                                {
                                        do_read(ec);
                                }
                        });
                }
                else
                {
                        std::cout << "in do_write error" << std::endl;
                }
        }

        tcp::socket socket_;
        enum { max_length = 1024 };
        char data_[max_length];
        std::string message_;
};

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

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

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

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

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

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

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

        try
        {

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

                io.run();

        }
        catch (std::exception &e)
        {
                std::cerr << e.what() << std::endl;
        }


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



//move完后,原先的值是未定义的状态,相当于是被初始化了。
class ObjectWithMove {
public:
        ObjectWithMove(int a) : m_a(a), m_p(p) {}
        ObjectWithMove(ObjectWithMove&& lhs) : m_a(lhs.m_a), m_p(lhs.m_p) {
                lhs.m_a = 0;
                lhs.m_p = nullptr;
        }
private:
        int m_a;
        int *m_p;
};

用C#写了个简单的客户端来测试:
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System;

public class Test002 {
        
        private static byte[] result = new byte[1024];  
        
        int main () {
                
                //设定服务器IP地址  
                IPAddress ip = IPAddress.Parse("127.0.0.1");  
                Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
                try  
                {  
                        clientSocket.Connect(new IPEndPoint(ip, 2001)); //配置服务器IP与端口  
                        Console.WriteLine("连接服务器成功");  
                }  
                catch  
                {  
                        Console.WriteLine("连接服务器失败,请按回车键退出!");  
                        return;  
                }  
                //通过clientSocket接收数据  
                int receiveLength = clientSocket.Receive(result);  
                Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));
                //通过 clientSocket 发送数据  
                for (int i = 0; i < 3; i++)  
                {  
                        try  
                        {  
                                Thread.Sleep(1000);    //等待1秒钟  
                                string sendMessage = "client send Message Hellp" + DateTime.Now;  
                                clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));  
                                Console.WriteLine("向服务器发送消息:" + sendMessage);  
                                
                                receiveLength = clientSocket.Receive(result);  
                                Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));

                        }  
                        catch  
                        {  
                                Console.WriteLine("与服务器断开连接");  
                                clientSocket.Shutdown(SocketShutdown.Both);  
                                clientSocket.Close();  
                                break;  
                        }  
                }  
                Console.WriteLine("发送完毕,按回车键退出");   
                
        }
        
}

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

评分

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

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-1-19 17:06

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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