Frage

Ich schreibe Server mit Boost.asio. Ich habe für jede Verbindung Puffer gelesen und schreibe und verwende asynchronisierte Lese-/Schreibfunktion (async_write_some / async_read_some).

Mit Lesepuffer und async_read_some, es gibt keine Probleme. Nur aufrufen async_read_some Die Funktion ist in Ordnung, weil Lesepuffer nur in Read -Handler gelesen wird (bedeutet normalerweise im selben Thread).

Schreibpuffer muss jedoch von mehreren Threads zugegriffen werden, damit er für die Änderung gesperrt werden muss.

ERSTE FRAGE!

Gibt es eine Möglichkeit, Sperren für Schreibpuffer zu vermeiden?

Ich schreibe mein eigenes Paket in Stack Puffer und kopiere es in den Schreibpuffer. Dann ruf an async_write_some Funktion zum Senden des Pakets. Auf diese Weise ist es in Ordnung, wenn ich zwei Paket in Serie sende, aufgerufen async_write_some Zweimal funktionieren?

ZWEITE FRAGE!

Was ist ein gemeinsamer Weg für asynchronisiertes Schreiben in Socket -Programmierung?

Danke fürs Lesen.

War es hilfreich?

Lösung

Antwort 1:

Sie haben Recht, dass das Sperren ein praktikabler Ansatz ist, aber es gibt eine viel einfachere Möglichkeit, all dies zu tun. Boost hat ein schönes kleines Konstrukt in Asio namens a strand. Jeder Rückruf, der mit dem Strang eingewickelt wurde, wird serialisiert, garantiert, unabhängig davon, welcher Thread den Rückruf ausführt. Grundsätzlich behandelt es jede Verriegelung für Sie.

Dies bedeutet, dass Sie so viele Schriftsteller haben können, wie Sie wollen, und wenn sie alle von der verpackt sind gleich Strand (also teilen Sie Ihren einzelnen Strang unter all Ihren Autoren), sie werden seriell ausführen. Eine Sache, auf die man achten muss, ist sicherzustellen, dass Sie nicht versuchen, den gleichen tatsächlichen Puffer im Speicher zu verwenden, um alle Schreibvorgänge zu machen. Dies ist beispielsweise zu vermeiden:

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);

Dort teilen Sie Ihren tatsächlichen Schreibpuffer (buffer_to_write). Wenn Sie stattdessen so etwas getan haben, werden Sie in Ordnung sein:

/* 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);

Hier würde es helfen, wenn Sie Ihren Strang auch in WritePacket übergeben würden. Sie haben jedoch die Idee.

Antwort #2:

Ich denke, Sie verfolgen bereits einen sehr guten Ansatz. Ein Vorschlag, den ich anbieten würde, ist die Verwendung async_write Anstatt von async_write_some Damit Sie garantiert sind, dass der gesamte Puffer geschrieben wird, bevor Ihr Rückruf angerufen wird.

Andere Tipps

Entschuldigung, aber Sie haben zwei Möglichkeiten:

  1. Serialisieren Sie die Schreibanweisung entweder mit Schlössern oder besser einen separaten Schriftsteller -Thread mit Anforderungen einer Warteschlange. Andere Threads können dann ohne allzu große Streitanforderungen in der Warteschlange stapeln (einige Mutexing wäre erforderlich).

  2. Geben Sie jedem Schreibfaden einen eigenen Sockel! Dies ist tatsächlich die bessere Lösung, wenn das Programm am anderen Ende des Kabels es unterstützen kann.

Sie können Ihre Änderungen anstellen und sie für die Daten im Write -Handler ausführen.

Das Netzwerk wäre höchstwahrscheinlich der langsamste Teil des Rohrs (vorausgesetzt, Ihre Änderung ist nicht rechnerisch teuer), sodass Sie Mods durchführen können, während die Socket -Ebene die vorherigen Daten sendet.

Wenn Sie eine große Anzahl von Clients mit häufigem Connect/Disconnect bearbeiten, schauen Sie sich die IO -Abschlussanschlüsse oder einen ähnlichen Mechanismus an.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top