Question

Je suis serveur écrit en utilisant boost.asio. J'ai lu et un tampon pour chaque connexion et l'utilisation asynchronized écriture lecture / fonction d'écriture (async_write_some / async_read_some).

Avec tampon de lecture et async_read_some, il n'y a pas de problème. Il suffit d'invoquer la fonction async_read_some est acceptable parce que mémoire tampon de lecture est en lecture seule dans le gestionnaire de lecture (signifie en même fil habituellement).

Mais, écrivez tampon doit être accessible à partir de plusieurs threads il doit être verrouillé pour modifier.

PREMIERE QUESTION!

Y a-t-il une façon d'éviter BLOCAGE pour le tampon d'écriture?

J'écris mon propre paquet dans le tampon de la pile et le copier dans la mémoire tampon d'écriture. Ensuite, appelez la fonction async_write_some pour envoyer le paquet. De cette façon, si j'envoie deux paquets en série, est-il fonction async_write_some invocateur bien deux fois?

DEUXIEME QUESTION!

Qu'est-ce chemin commun pour l'écriture asynchronized dans la programmation socket?

Merci pour la lecture.

Était-ce utile?

La solution

Réponse # 1:

Vous avez raison que le verrouillage est une approche viable, mais il y a un moyen de faire tout cela beaucoup plus simple. Boost a une jolie petite construction dans ASIO appelé strand. Tout rappel qui a été emballé à l'aide du fil sera sérialisé, garanti, quel que soit le thread exécute le rappel. Fondamentalement, il gère tout blocage pour vous.

Cela signifie que vous pouvez avoir autant d'écrivains que vous voulez, et s'ils sont tous emballés par le même brin (donc, partager votre seul brin parmi tous vos écrivains) ils exécuteront en série . Une chose à surveiller est de vous assurer que vous n'êtes pas essayez d'utiliser le même tampon réelle en mémoire pour faire toutes les écritures. Par exemple, voici ce qu'il faut éviter:

char buffer_to_write[256];  // shared among threads

/* ... in thread 1 ... */
memcpy(buffer_to_write, packet_1, std::min(sizeof(packet_1), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

/* ... in thread 2 ... */
memcpy(buffer_to_write, packet_2, std::min(sizeof(packet_2), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

, vous partagez votre tampon d'écriture réelle (buffer_to_write). Si vous avez fait quelque chose comme ça à la place, vous serez d'accord:

/* A utility class that you can use */
class PacketWriter
{
private:
  typedef std::vector<char>  buffer_type;

  static void WriteIsComplete(boost::shared_ptr<buffer_type> op_buffer, const boost::system::error_code& error, std::size_t bytes_transferred)
  {
    // Handle your write completion here
  }

public:
  template<class IO>
  static bool WritePacket(const std::vector<char>& packet_data, IO& asio_object)
  {
    boost::shared_ptr<buffer_type> op_buffer(new buffer_type(packet_data));

    if (!op_buffer)
    {
      return (false);
    }

    asio_object.async_write_some(boost::asio::buffer(*op_buffer), boost::bind(&PacketWriter::WriteIsComplete, op_buffer, boost::asio::placeholder::error, boost::asio::placeholder::bytes_transferred));
  }
};

/* ... in thread 1 ... */
PacketWriter::WritePacket(packet_1, my_socket);

/* ... in thread 2 ... */
PacketWriter::WritePacket(packet_2, my_socket);

Ici, il serait utile si vous avez passé votre brin dans WritePacket ainsi. Vous avez l'idée, cependant.

Réponse # 2:

Je pense que vous prenez déjà une très bonne approche. Une suggestion que je voudrais proposer est d'utiliser async_write au lieu de async_write_some de sorte que vous êtes assuré toute la mémoire tampon est écrit avant que votre rappel est appelée.

Autres conseils

Désolé, mais vous avez deux choix:

  1. serialise l'instruction d'écriture, que ce soit avec des serrures, ou mieux lancer un thread d'écriture séparé qui lit les demandes de une file d'attente, d'autres fils peuvent ensuite empiler les requêtes sur le file d'attente sans contention trop (certains mutexing serait nécessaire).

  2. Donner à chaque écriture enfilez sa propre prise! Ceci est en fait la meilleure solution si le programme à l'autre extrémité du fil peut supporter.

Vous pouvez la file d'attente vos modifications et les exécuter sur les données dans le gestionnaire d'écriture.

Réseau serait très probablement la partie la plus lente de la conduite (en supposant que votre modification ne sont pas computationaly cher), pour que vous puissiez effectuer des mods tandis que la couche socket est l'envoi de la les données précédentes.

Incase vous gérez grand nombre de clients avec connexion / déconnexion fréquente un coup d'oeil à IO Les ports de terminaison ou un mécanisme similaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top