Если вы хотите отправлять / получать сообщения, которые не имеют фиксированного размера, вы можете использовать подход, при котором вы определяете заголовок вашего сообщения, например, с 4-байтовым полем для хранения размера содержимого вашего сообщения:
[header(4 bytes) to store the size of message][content of message]
тогда вы всегда знаете, что первым шагом является чтение 4 байтов, подготовка буфера для данных и чтение дальнейших данных до тех пор, пока буфер не будет заполнен.
Другой способ - выключение сокет (ниже псевдокод)
The receiving side | the sending side
--------------------------------------------------------------------------------
error_code ec; |
asio::read(sock,buf,ec) [2] |
| prepare some buffer with unknown size
| string buf;
| buf += ...; // add data
| asio::write(sock,buf)
| sock.shutdown(socket_base::shutdown_send); [1]
, позвонив sock.shutdown()
на отправляющей стороне [1], вы можете сообщить получающей стороне, что все сообщение было отправлено.Затем на принимающей стороне после прочтения сообщения [2] необходимо проверить состояние переменной ec
error-code, является ли она boost::asio::eof
.Если вы получите end-of-file
, вы знаете, что сообщение завершено.Если ec
отличается от eof
, это означает, что произошла ошибка.
Начиная с версии повышения версии 1.66 вы можете использовать dynamic_buffer
для хранения данных, она адаптирует строку или вектор в качестве буфера.Или вы можете рассмотреть streambuf
для чтения нефиксированных буферов.
EDIT
Добавлено использование dynamic_buffer
.В соответствии со ссылкой dynamic_buffer
может использоваться в свободных функциях, таких как async_read
, async_until
, но не в async_read_some
в качестве функции-члена сокета.Ниже приведены коды клиента и сервера:
Сервер:
using namespace boost;
struct Data {
std::shared_ptr<asio::ip::tcp::socket> sock;
std::string buf; // buf is empty [1]
};
void readHandler (
const boost::system::error_code& ec,
size_t length,
std::shared_ptr<Data> d) {
std::cout << "readHandler" << std::endl;
if (ec == boost::asio::error::eof)
{
// here we got the whole message
std::cout << d->buf << std::endl;
}
else
{
std::cout << "Error" << std::endl;
}
}
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),9999);
asio::io_service ios;
asio::ip::tcp::acceptor acceptor(ios, ep);
std::shared_ptr<asio::ip::tcp::socket> sock{new asio::ip::tcp::socket(ios)};
acceptor.accept(*sock);
std::shared_ptr<Data> data(new Data);
data->sock = move(sock);
boost::asio::async_read (*(data->sock), asio::dynamic_buffer(data->buf),
std::bind(readHandler,std::placeholders::_1, std::placeholders::_2,data)); // [2]
ios.run(); // wait until async_write is complete
}
catch (system::system_error &e) {
std::cout << "error " << e.what() << std::endl;
}
return 0;
}
в [1] мы создаем пустой буфер как строковый объект, в [2] мы вызываем async_read
для получения данныхиспользуя dynamic_buffer
.Обработчик, переданный в async_read
, вызывается, когда отправляющая сторона закрывает сокет на своей стороне.
Клиент:
using namespace boost;
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string("127.0.0.1"),9999);
asio::io_service ios;
asio::ip::tcp::socket sock(ios, ep.protocol());
sock.connect(ep);
std::string buf = "some message";
for (int i = 0; i < buf.size(); ++i) {
// synchronous function was used to make simpler code
asio::write(sock,asio::buffer(buf.c_str()+i,1)); // send 1 char
std::this_thread::sleep_for(std::chrono::seconds(1)); // delay 1 second
}
sock.shutdown(asio::socket_base::shutdown_send);
}
catch (system::system_error &e) {
std::cout << "Error " << e.what() << std::endl;
}
return 0;
}
, как вы можете видеть, мы отправляем символ за символом из строки свторая задержкаПоэтому, когда вы запускаете сервер, а затем клиент, сервер должен получить сообщение целиком через ~ 12 секунд.async_read
ожидает на сервере, пока не придет eof
- он отправляется клиентом по вызову shutdown
на сокете.