2010-05-07 6 views
11

Tôi đang học Boost :: asio và tất cả những thứ không đồng bộ. Làm thế nào tôi có thể đọc không đồng bộ với biến số user_ của loại std :: string? Boost::asio::buffer(user_) chỉ hoạt động với async_write(), nhưng không hoạt động với async_read(). Nó hoạt động với vector, vậy lý do cho nó không làm việc với chuỗi là gì? Có cách nào khác để làm điều đó ngoài việc khai báo char user_[max_len] và sử dụng Boost::asio::buffer(user_, max_len)?Cách đọc không đồng bộ đến std :: string using Boost :: asio?

Ngoài ra, điểm kế thừa từ boost::enable_shared_from_this<Connection> và sử dụng shared_from_this() thay vì this trong async_read()async_write() là gì? Tôi đã thấy rất nhiều trong các ví dụ.

Đây là một phần của mã của tôi:

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) 
      { 
       disconnect(); 
      } 
      else 
      { 
       async_read(socket_, boost::asio::buffer(user_), 
        boost::bind(&Connection::handle_user_read, this, 
        placeholders::error, placeholders::bytes_transferred)); 
      } 
     } 

     void handle_user_read(const boost::system::error_code& err, 
      std::size_t bytes_transferred) 
     { 
      if (err) 
      { 
       disconnect(); 
      } 
      else 
      { 
       ... 
      } 
     } 

     ... 

     void disconnect() 
     { 
      socket_.shutdown(tcp::socket::shutdown_both); 
      socket_.close(); 
      socket_.open(tcp::v4()); 
      start_accept(); 
     } 

     tcp::acceptor &acceptor_; 
     tcp::socket socket_; 
     std::string user_; 
     std::string pass_; 
     ... 
}; 

Trả lời

30

Các tài liệu Boost.Asio khẳng định:

Một đối tượng đệm đại diện cho một khu vực tiếp giáp của bộ nhớ như một 2-tuple chứa một con trỏ và kích thước tính bằng byte. Một tuple của biểu mẫu {void *, size_t} chỉ định vùng bộ nhớ có thể thay đổi (có thể sửa đổi).

Điều này có nghĩa là để gọi tới async_read để ghi dữ liệu vào bộ đệm, phải là (trong đối tượng bộ đệm bên dưới) khối liên tiếp của bộ nhớ. Ngoài ra, đối tượng bộ đệm phải có khả năng ghi vào khối bộ nhớ đó.

std::string không cho phép tùy ý ghi vào bộ đệm của nó, vì vậy async_read không thể viết khối bộ nhớ vào bộ đệm của một chuỗi (lưu ý rằng std::stringkhông cung cấp cho người gọi chỉ đọc truy cập vào bộ đệm cơ bản thông qua phương thức data(), mà đảm bảo rằng con trỏ trả về sẽ có giá trị cho đến khi cuộc gọi tiếp theo đến một hàm không phải thành viên. Vì lý do này, Asio có thể dễ dàng tạo một const_buffer gói một std::string và bạn có thể sử dụng nó với async_write).

Tài liệu Asio có mã ví dụ cho chương trình "trò chuyện" đơn giản (xem http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat) có phương pháp tốt để khắc phục sự cố này. Về cơ bản, bạn cần gửi TCP gửi theo kích cỡ của thư đầu tiên, trong tiêu đề "" của loại, và trình xử lý đọc của bạn phải giải thích tiêu đề để cấp phát bộ đệm có kích thước cố định phù hợp để đọc dữ liệu thực.

Theo nhu cầu sử dụng shared_from_this() trong async_readasync_write, lý do là đảm bảo rằng phương pháp được bao bọc bởi boost::bind sẽ luôn đề cập đến một đối tượng trực tiếp. Hãy xem xét các tình huống sau đây:

  1. phương pháp handle_accept bạn gọi async_read và gửi một handler "vào lò phản ứng" - về cơ bản bạn đã hỏi những io_service để gọi Connection::handle_user_read khi nó kết thúc đọc dữ liệu từ các ổ cắm. Các io_service lưu trữ functor này và tiếp tục vòng lặp của nó, chờ đợi cho các hoạt động đọc không đồng bộ để hoàn thành.
  2. Sau khi bạn gọi tới số async_read, đối tượng Connection được xử lý vì một số lý do (chấm dứt chương trình, điều kiện lỗi, v.v.)
  3. Giả sử io_service nay xác định rằng đọc không đồng bộ hoàn tất, sau đối tượng Connection đã được deallocated nhưng trước các io_service bị phá hủy (điều này có thể xảy ra, ví dụ, nếu io_service::run đang chạy trong một thread riêng biệt, như là điển hình). Bây giờ, các nỗ lực io_service để gọi trình xử lý và nó có tham chiếu không hợp lệ đối tượng Connection.

Giải pháp là phải phân bổ Connection qua một shared_ptr và sử dụng shared_from_this() thay vì this khi gửi một handler "vào lò phản ứng" - điều này cho phép io_service để lưu trữ một tham chiếu chung cho các đối tượng, và shared_ptr đảm bảo rằng nó đã giành' t được deallocated cho đến khi tài liệu tham khảo cuối cùng hết hạn.

Vì vậy, mã của bạn có lẽ nên nhìn cái gì đó như:

class Connection : public boost::enable_shared_from_this<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, shared_from_this())); 
    } 

private: 

    void start_accept() 
    { 
     acceptor_.async_accept(socket_, 
      boost::bind(&Connection::handle_accept, shared_from_this(), 
      placeholders::error)); 
    } 

    void handle_accept(const boost::system::error_code& err) 
    { 
     if (err) 
     { 
      disconnect(); 
     } 
     else 
     { 
      async_read(socket_, boost::asio::buffer(user_), 
       boost::bind(&Connection::handle_user_read, shared_from_this(), 
       placeholders::error, placeholders::bytes_transferred)); 
     } 
    } 
    //... 
}; 

Lưu ý rằng bây giờ bạn phải đảm bảo rằng mỗi đối tượng Connection được phân bổ thông qua một shared_ptr, ví dụ:

boost::shared_ptr<Connection> new_conn(new Connection(...)); 

Hope this helps !

8

này không được thiết kế để có một câu trả lời cho mỗi gia nhập, nhưng chỉ cần một lời nhận xét dài: một cách rất đơn giản để chuyển đổi từ một bộ đệm ASIO thành một chuỗi được stream từ nó:

asio::streambuf buff; 
asio::read_until(source, buff, '\r'); // for example 

istream is(&buff); 
is >> targetstring; 

Đây là một bản sao dữ liệu, tất nhiên, nhưng đó là những gì bạn cần làm nếu bạn muốn nó trong một chuỗi.

4

Bạn có thể sử dụng một std:string với async\_read() như thế này:

async_read(socket_, boost::asio::buffer(&user_[0], user_.size()), 
      boost::bind(&Connection::handle_user_read, this, 
      placeholders::error, placeholders::bytes_transferred)); 

Tuy nhiên, bạn nên chắc chắn rằng std::string là đủ lớn để chấp nhận các gói tin mà bạn đang mong đợi và đệm bằng số không trước khi gọi async\_read().

Và như cho lý do tại sao bạn nên KHÔNG BAO GIỜ ràng buộc một hàm thành viên gọi lại để một con trỏ this nếu đối tượng có thể bị xóa, mô tả đầy đủ hơn và một phương pháp mạnh mẽ hơn có thể được tìm thấy ở đây: Boost async_* functions and shared_ptr's.

0

Tăng Asio có hai kiểu bộ đệm. Có boost::asio::buffer(your_data_structure), trong đó không thể phát triển và do đó thường vô dụng đối với mục nhập không xác định và có boost::asio::streambuf trong đó có thể phát triển.

Cho một boost::asio::streambuf buf, bạn biến nó thành một chuỗi với std::string(std::istreambuf_iterator<char>(&buf), {});.

Điều này không hiệu quả khi bạn kết thúc sao chép dữ liệu một lần nữa, nhưng điều đó đòi hỏi phải làm cho boost::asio::buffer nhận biết các thùng chứa có thể trồng được, tức là các thùng chứa có phương pháp .resize(N). Bạn không thể làm cho nó hiệu quả mà không cần chạm vào Mã tăng cường.