Comment lire de manière asynchrone à std :: string en utilisant Boost :: asio?
Je suis en train d'apprendre Boost::asio et tout ce qui async choses. Comment puis-je lire de manière asynchrone pour la variable user_
de type std::string? Boost::asio::buffer(user_)
fonctionne uniquement avec async_write()
mais pas avec async_read()
. Il fonctionne avec un vecteur, quelle est donc la raison pour elle de ne pas travailler avec de la ficelle? Est-il une autre façon de le faire qu'en plus de déclarer char user_[max_len]
et à l'aide de Boost::asio::buffer(user_, max_len)
?
Aussi, ce qui est le point d'hériter de boost::enable_shared_from_this<Connection>
et à l'aide de shared_from_this()
au lieu de this
dans async_read()
et async_write()
? J'ai vu que beaucoup de choses dans les exemples.
Voici une partie de mon code:
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_;
...
};
source d'informationauteur SpyBot
Vous devez vous connecter pour publier un commentaire.
Le Coup De Pouce.Asio états de documentation:
Cela signifie que, pour un appel à
async_read
pour écrire des données dans une mémoire tampon, il doit être (dans le sous-jacentes de la mémoire tampon de l'objet) d'un bloc contigu de mémoire. En outre, le tampon de l'objet doit être en mesure d'écrire à ce bloc de mémoire.std::string
ne permet pas arbitraire écrit dans son mémoire tampon, de sorteasync_read
ne peut pas écrire des blocs de mémoire dans une chaîne de caractères du tampon (à noter questd::string
ne permet à l'appelant d'un accès en lecture seule au fond de la mémoire tampon via ledata()
méthode, qui garantit que le pointeur retourné sera valide jusqu'à ce que le prochain appel à un non-const fonction membre. Pour cette raison, Asio permet de créer facilement unconst_buffer
l'enchaînement d'unstd::string
et vous pouvez l'utiliser avecasync_write
).L'Asio de la documentation a un exemple de code pour un simple "chat" du programme (voir http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat) qui a une bonne méthode pour surmonter ce problème. Fondamentalement, vous devez avoir le TCP émetteur envoie le long de la taille d'un message d'abord, dans un "en-tête" de toutes sortes, et de lire votre gestionnaire doit interpréter l'en-tête d'allouer un tampon de taille fixe adapté pour la lecture des données réelles.
Autant que le besoin de l'aide de
shared_from_this()
dansasync_read
etasync_write
la raison est qu'il garantit que la méthode encapsulée parboost::bind
va toujours se référer à un objet live. Imaginons la situation suivante:handle_accept
les appels de méthodeasync_read
et envoie un gestionnaire "dans le réacteur" - en gros, vous avez demandé laio_service
d'invoquerConnection::handle_user_read
quand il a fini de lire les données à partir de la douille. Leio_service
magasins de ce foncteur et continue sa boucle d'attente pour l'opération de lecture asynchrone complète.async_read
leConnection
objet est libéré pour une raison quelconque (fin de programme, une condition d'erreur, etc.)io_service
détermine maintenant que la lecture asynchrone est terminée, après laConnection
objet a été libéré, mais avant laio_service
est détruit (ce qui peut se produire, par exemple, siio_service::run
s'exécute dans un thread séparé, comme c'est typique). Maintenant, laio_service
tente d'invoquer le gestionnaire, et il a une référence non valide pour unConnection
objet.La solution est de répartir
Connection
via unshared_ptr
et l'utilisationshared_from_this()
au lieu dethis
lors de l'envoi d'un gestionnaire "dans le réacteur" - ce qui permet àio_service
pour stocker une référence partagée à l'objet, etshared_ptr
garantit qu'il ne sera pas libéré jusqu'à ce que la dernière référence expire.Donc, votre code doit probablement ressembler à quelque chose comme:
Note que, maintenant, vous devez vous assurer que chaque
Connection
objet est alloué par l'intermédiaire d'unshared_ptr
par exemple:Espérons que cette aide!
Ce n'est pas destiné à être une réponse en soi, mais juste un long commentaire: un moyen très simple de convertir à partir d'une mémoire tampon à une chaîne en streaming:
C'est une copie de données, bien sûr, mais c'est ce que vous devez faire si vous le voulez dans une chaîne de caractères.
Vous pouvez utiliser un
std:string
avecasync\_read()
comme ceci:Cependant, vous feriez mieux de assurez-vous que le
std::string
est assez grand pour accepter le paquet que vous attendez et des zéros avant d'appelerasync\_read()
.Et que pour expliquer pourquoi vous devriez JAMAIS lier un membre de la fonction de rappel pour un
this
pointeur si l'objet peut être supprimé, une description plus complète et la plus solide, la méthode peut être trouvé ici: Boost async_* les fonctions et les shared_ptr est.Boost Asio a deux styles de tampons. Il y a
boost::asio::buffer(your_data_structure)
qui ne peut pas grandir, et est donc généralement inutiles pour l'inconnu d'entrée, et il n'y aboost::asio::streambuf
qui peut croître.Donné un
boost::asio::streambuf buf
vous transformer en une chaîne de caractères avecstd::string(std::istreambuf_iterator<char>(&buf), {});
.Ce n'est pas efficace comme vous vous retrouvez à la copie de données une fois de plus, mais cela impliquerait de faire
boost::asio::buffer
conscient de la cultivables conteneurs, conteneurs qui ont une.resize(N)
méthode. Vous ne pouvez pas être efficace sans toucher Boost code.