dnfpk001 发表于 2017-7-11 17:49:48

《C++Boost库asio》第八篇 Echo服务器


// 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_),
                        (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),
                                (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),
                                (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_;
        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_, (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;
       
        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++客户端在下一篇研究{:10_266:}
页: [1]
查看完整版本: 《C++Boost库asio》第八篇 Echo服务器