3

std::vector を送信できるように TCP サーバーとクライアントを実装しようとしましたが、これまでのところ失敗しました。Asio のブースト ドキュメントが (控えめに言っても) 不足しており、理解しにくい (英語は私の第一言語ではありません) ことがわかりました。

いずれにせよ、私は iostreams の例を見て、オブジェクト指向のソリューションを実装しようとしましたが、失敗しました。

実装しようとしているサーバーは、複数のクライアントからの接続を受け入れることができる必要があります (どうすればよいですか?)

サーバーは std::vector を受信し、 /* 何かを実行 */ し、それをクライアントに返して、サーバーがデータをそのまま受信したことをクライアントが認識できるようにする必要があります。

*.h ファイル

class TCP_Server : private boost::noncopyable
        {
            typedef boost::shared_ptr<TCP_Connection> tcp_conn_pointer;

            public :
                TCP_Server(ba::io_service &io_service, int port);
                virtual ~TCP_Server() {}
                virtual void Start_Accept();
            private:
                virtual void Handle_Accept(const boost::system::error_code& e);
            private :
                int                 m_port;
                ba::io_service&     m_io_service;               // IO Service
                bi::tcp::acceptor   m_acceptor;         // TCP Connections acceptor
                tcp_conn_pointer    m_new_tcp_connection;   // New connection pointer
        };

*.cpp ファイル

TCP_Server::TCP_Server(boost::asio::io_service &io_service, int port) : 
            m_io_service(io_service), 
            m_acceptor(io_service, bi::tcp::endpoint(bi::tcp::v4(), port)), 
            m_new_tcp_connection(TCP_Connection::Create(io_service))
        {
            m_port = port;
            Start_Accept();
        }

        void TCP_Server::Start_Accept()
        {
            std::cout << "[TCP_Server][Start_Accept] => Listening on port : " << m_port << std::endl;
            //m_acceptor.async_accept(m_new_tcp_connection->Socket(),
            //                        boost::bind(&TCP_Server::Handle_Accept, this,
            //                                    ba::placeholders::error));


                m_acceptor.async_accept(*m_stream.rdbuf(),
                boost::bind(&TCP_Server::Handle_Accept, 
                this,
                ba::placeholders::error));
        }

        void TCP_Server::Handle_Accept(const boost::system::error_code &e)
        {
            if(!e)
            {

                /*boost::thread T(boost::bind(&TCP_Connection::Run, m_new_tcp_connection));
                std::cout << "[TCP_Server][Handle_Accept] => Accepting incoming connection. Launching Thread " << std::endl;
                m_new_tcp_connection = TCP_Connection::Create(m_io_service);
                m_acceptor.async_accept(m_new_tcp_connection->Socket(), 
                                        boost::bind(&TCP_Server::Handle_Accept, 
                                                    this, 
                                                    ba::placeholders::error));*/
                m_stream << "Server Response..." << std::endl;
            }
        }

クライアントはどのように見えるべきですか?両方のアプリが「会話」している間、接続を維持するにはどうすればよいですか?

4

3 に答える 3

3

AFAIK ASIO iostreams は、同期 I/O 専用です。しかし、あなたの例は、非同期 I/O を使用したいというヒントを与えてくれます。以下は、非同期 I/O を使用して、リクエスト内の整数の 4 バイト カウントが前に付いた整数の配列で構成されるリクエストを読み取るサーバーの小さな例です。したがって、実際には、整数のベクトルを count(4 バイト) int int ... としてシリアル化しています。int のベクトルの読み取りが成功した場合、サーバーは 4 バイトの応答コード (=1) を書き込み、次に read を発行します。クライアントからの新しいリクエスト。コードは次のとおりです。

#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>

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

class Connection
{
public:
    Connection(tcp::acceptor& acceptor)
        : acceptor_(acceptor), socket_(acceptor.get_io_service(), tcp::v4())
    {
    }
    void start()
    {
        acceptor_.get_io_service().post(boost::bind(&Connection::start_accept, this));
    }
private:
    void start_accept()
    {
        acceptor_.async_accept(socket_,boost::bind(&Connection::handle_accept, this, 
            placeholders::error));
    }
    void handle_accept(const boost::system::error_code& err)
    {
        if (err)
        {
            //Failed to accept the incoming connection.
            disconnect();
        }
        else
        {
            count_ = 0;
            async_read(socket_, buffer(&count_, sizeof(count_)),
                boost::bind(&Connection::handle_read_count,
                this, placeholders::error, placeholders::bytes_transferred));
        }
    }
    void handle_read_count(const boost::system::error_code& err, std::size_t bytes_transferred)
    {
        if (err || (bytes_transferred != sizeof(count_))
        {
            //Failed to read the element count.
            disconnect();
        }
        else
        {
            elements_.assign(count_, 0);
            async_read(socket_, buffer(elements_), 
                boost::bind(&Connection::handle_read_elements, this,
                placeholders::error, placeholders::bytes_transferred));
        }
    }

    void handle_read_elements(const boost::system::error_code& err, std::size_t bytes_transferred)
    {
        if (err || (bytes_transferred != count_ * sizeof(int)))
        {
            //Failed to read the request elements.
            disconnect();
        }
        else
        {
            response_ = 1;
            async_write(socket_, buffer(&response_, sizeof(response_)),
                boost::bind(&Connection::handle_write_response, this,
                placeholders::error, placeholders::bytes_transferred));
        }
    }
    void handle_write_response(const boost::system::error_code& err, std::size_t bytes_transferred)
    {
        if (err)
            disconnect();
        else
        {
            //Start a fresh read 
            count_ = 0;
            async_read(socket_, buffer(&count_, sizeof(count_)),
                boost::bind(&Connection::handle_read_count,
                this, placeholders::error, placeholders::bytes_transferred));
        }
    }
    void disconnect()
    {
        socket_.shutdown(tcp::socket::shutdown_both);
        socket_.close();
        socket_.open(tcp::v4());
        start_accept();
    }
    tcp::acceptor& acceptor_;
    tcp::socket socket_;
    std::vector<int> elements_;
    long count_;
    long response_;
};

class Server : private boost::noncopyable
{
public:
    Server(unsigned short port, unsigned short thread_pool_size, unsigned short conn_pool_size)
        : acceptor_(io_service_, tcp::endpoint(tcp::v4(), port), true)
    {
        unsigned short i = 0;
        for (i = 0; i < conn_pool_size; ++i)
        {
            ConnectionPtr conn(new Connection(acceptor_));
            conn->start();
            conn_pool_.push_back(conn);
        }

        // Start the pool of threads to run all of the io_services.
        for (i = 0; i < thread_pool_size; ++i)
        {
            thread_pool_.create_thread(boost::bind(&io_service::run, &io_service_));
        }
    }   
    ~Server()
    {
        io_service_.stop();
        thread_pool_.join_all();
    }

private:
    io_service io_service_;
    tcp::acceptor acceptor_;
    typedef boost::shared_ptr<Connection> ConnectionPtr;
    std::vector<ConnectionPtr> conn_pool_;
    boost::thread_group thread_pool_;
};

boost::function0<void> console_ctrl_function;

BOOL WINAPI console_ctrl_handler(DWORD ctrl_type)
{
  switch (ctrl_type)
  {
  case CTRL_C_EVENT:
  case CTRL_BREAK_EVENT:
  case CTRL_CLOSE_EVENT:
  case CTRL_SHUTDOWN_EVENT:
    console_ctrl_function();
    return TRUE;
  default:
    return FALSE;
  }
}

void stop_server(Server* pServer)
{
    delete pServer;
    pServer = NULL;
}

int main()
{
    Server *pServer = new Server(10255, 4, 20);
    console_ctrl_function = boost::bind(stop_server, pServer);
    SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
    while(true)
    {
        Sleep(10000);
    }
}
于 2009-07-13T14:28:58.537 に答える
1

両端の通信がC++で実現されている場合は、Boost Serializationライブラリを使用して、ベクトルをバイトにセジライズし、これらを他のマシンに転送できます。反対側では、ブーストシリアル化ライブラリを使用してオブジェクトをdesirializeします。私は少なくとも2つのアプローチがそうしているのを見ました。

ブーストシリアル化の利点:このアプローチは、32ビットシステムと64ビットシステムの間でオブジェクトを転送する場合にも機能します。

以下はリンクです:
コードプロジェクトの記事
ブーストメーリングリストのアイデア

よろしく、
Ovanes

于 2009-07-11T11:45:28.817 に答える
1

あなたが投稿したコードは少し不完全/間違っていると思います。それにもかかわらず、ここにいくつかのガイダンスがあります..

1) async_accept() 呼び出しが間違っているようです。それは次のようなものでなければなりません、

m_acceptor.async_accept(m_new_tcp_connection->socket(),...)

2)ソケットが受け入れられた、Handle_Accept() 関数が呼び出されることに注意してください。つまり、制御が Handle_Accept() に達したら、ソケットに書き込むだけです。何かのようなもの

void TCP_Server::Handle_Accept(const system::error_code& error)
{
  if(!error)
  {
    //send data to the client
    string message = "hello there!\n";

    //Write data to the socket and then call the handler AFTER that
    //Note, you will need to define a Handle_Write() function in your TCP_Connection class.
async_write(m_new_tcp_connection->socket(),buffer(message),bind(&TCP_Connection::Handle_Write, this,placeholders::error,placeholders::bytes_transferred)); 

    //accept the next connection
    Start_Accept();
  }
}

3) クライアントについては、こちらをご覧ください: http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/tutorial/tutdaytime1.html

于 2009-06-29T23:32:27.740 に答える