2012-09-03 11 views
5

Ich benutze den gleichen Socket in meinem UDP-Server, um Daten von Clients auf einem Port und später nach der Verarbeitung von Anfragen an Clients mit IP :: ud :: socket reagieren :: async_send_toVerwenden Sie den gleichen Udp-Socket für Async empfangen/senden

Der Empfang erfolgt asynchron mit async_receive_from. Der Socket verwendet denselben ioService (es ist der gleiche Socket nach allem) Die Dokumentation sagt nicht klar, ob man im selben Moment den gleichen Udp-Socket Datagramme von Client A (asynchron) empfangen und möglicherweise ein anderes Datagramm an Client B senden kann (Async gesendet) zur gleichen Zeit Ich vermute, dass dies zu Problemen führen könnte. Ich habe am Ende den gleichen Socket für die Antwort verwendet, weil ich keinen anderen Socket an denselben Server-Port binden konnte, während ich auf einen anderen Client antwortete.

Wie kann ich einen anderen Socket an den gleichen Server-Port binden?

BEARBEITEN. Ich versuche, den zweiten udp-Buchse mit dem gleichen UDP-Port zu binden, mit:

socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)) 

Als ich dieses ersten Mal tun (verbindlich für Server „empfangen“ socket) ist es in Ordnung, aber versuchen, eine anderen Sockel zum zweiten Mal, so zu schaffen Es meldet einen Fehler bei der Bindung (asio löst Ausnahme aus)

+2

Bitte geben Sie Ihre aktuelle Frage an. Funktioniert Ihr Setup und Sie suchen nach einer Bestätigung, dass es vom Design her in Ordnung ist? Oder funktioniert es nicht, und wenn nicht, welchen Fehler beobachten Sie? Oder fragen Sie, wie man einen anderen Socket an den Server-Port bindet? – mtrw

+0

@mtrw Ist es möglich, einen anderen Socket an denselben Server-Port zu binden? Auf diese Weise würde ich verschiedene Sockets für das Senden von Wiederholungen verwenden. Was passiert ist, ist, dass ich denke, dass mein Setup eine Fehlfunktion des Servers verursachen kann. – Ghita

+0

Bitte bearbeiten Sie Ihre Frage zu klären, anstatt Kommentare hinzuzufügen –

Antwort

14

Es ist möglich, dass ein UDP-Socket gleichzeitig von einem Remote-Endpunkt empfängt und an einen anderen Remote-Endpunkt sendet. Laut der Boost.Asio-Dokumentation Threads and Boost.Asio ist es jedoch generell unsicher, gleichzeitige Aufrufe für ein einzelnes Objekt durchzuführen.

Somit ist dies sicher:

 thread_1        | thread_2 
--------------------------------------+--------------------------------------- 
socket.async_receive_from(...);  | 
socket.async_send_to(...);   |

und das ist sicher:

 thread_1        | thread_2 
--------------------------------------+--------------------------------------- 
socket.async_receive_from(...);  | 
             | socket.async_send_to(...);

aber dies ist angegeben als nicht sicher zu sein:

 thread_1        | thread_2 
--------------------------------------+--------------------------------------- 
socket.async_receive_from(...);  | socket.async_send_to(...); 
             |

Beachten Sie, dass einige Funktionen wie boost::asio::async_read, sind eine zusammengesetzte Operation, und haben zusätzliche Thread-Sicherheitsbeschränkungen.


Wenn eine der folgenden Bedingungen erfüllt sind, dann keine zusätzliche Synchronisation muss erfolgen, da die Strömung implizit synchron sein wird:

  • Alle Socket-Aufrufe innerhalb Handler auftreten, und io_service::run() wird nur aufgerufen, von ein einzelner Thread.
  • async_receive_from und async_send_to werden nur innerhalb derselben Kette asynchroner Operationen aufgerufen. Zum Beispiel ruft der , der an übergeben wurde, async_send_to auf, und der WriteHandler, der an async_send_to übergeben wird, ruft async_receive_from auf.

    void read() 
    { 
        socket.async_receive_from(..., handle_read); --. 
    }             | 
        .-----------------------------------------------' 
        |  .----------------------------------------. 
        V  V          | 
    void handle_read(...)        | 
    {             | 
        socket.async_send_to(..., handle_write); --. | 
    }            | | 
        .-------------------------------------------' | 
        |            | 
        V            | 
    void handle_write(...)       | 
    {             | 
        socket.async_receive_from(..., handle_read); --' 
    } 
    

Auf der anderen Seite, wenn es mehrere Threads potentiell machen gleichzeitige Anrufe an die Steckdose, dann muss die Synchronisation auftreten.Ziehen Sie in Betracht, die Synchronisierung auszuführen, indem Sie entweder die Funktionen und Handler über eine boost::asio::io_service::strand aufrufen oder andere Synchronisierungsmechanismen wie Boost.Thread mutex verwenden.


Zusätzlich zur Threadsicherheit muss die Verwaltung der Objektlebensdauern berücksichtigt werden. Wenn der Server mehrere Anforderungen gleichzeitig verarbeiten muss, achten Sie darauf, dass buffer und endpoint für jede Anfrage-> Prozess-> Antwort Kette die Eigentümer sind. Gemäß der Dokumentation von async_receive_from behält der Anrufer sowohl den Puffer als auch den Endpunkt. Daher kann es einfacher sein, die Lebensdauer der Objekte über boost::shared_ptr zu verwalten. Andernfalls, wenn die Kette schnell genug ist, dass keine gleichzeitigen Ketten erforderlich sind, vereinfacht dies die Verwaltung, wobei Puffer und Endpunkt pro Anforderung verwendet werden können.


Schließlich ist die socket_base::reuse_address Klasse ermöglicht eine Steckdose an eine Adresse gebunden zu sein, die bereits in Gebrauch ist. Aber ich glaube nicht, dass eine anwendbare Lösung ist hier, wie es allgemein verwendet wird:

  • Für TCP ein Prozess auf den gleichen Port, auch wenn der Port in einem TIME_WAIT Zustand neu zu starten und hören zu können.
  • Für UDP, damit mehrere Prozesse an denselben Port gebunden werden können, sodass jeder Prozess Multicast empfangen und übertragen kann.
+0

Ziemlich beeindruckende Antwort. I mein Fall Ich habe wie gesagt nur einen ioService-Thread (main) aber obwohl async_receive_from/async_send_to nicht interleaved genannt werden kann, kann einer von ihnen aufgerufen werden, bevor der andere abgeschlossen ist (vollständiger Handler für den anderen aufgerufen). Dies liegt daran, dass ich so in einem anderen Thread, der nichts mit ioService thread zu tun hat, so verfahren habe. – Ghita

+0

Eine andere Sache. Wo ist festgelegt, dass Endpunktklassen in einer bestimmten Weise behandelt werden müssen. Sobald ich einen Endpunkt als Ausgabe-Parameter in async_receive_from erhalte, mache ich Kopien (unter Verwendung des Kopierkonstruktors) und übergebe es als "Zustand" (so dass ich weiß, wohin ich später antworten soll) bis zu dem Moment, an dem ich die Antwort zurücksenden muss. – Ghita

+2

Es ist in Ordnung, wenn mehrere asynchrone Operationen für ein Objekt ausstehen; Es wird nur angegeben, dass gleichzeitige Aufrufe des Objekts unsicher sind. Wenn Socket-Aufrufe außerhalb eines Handlers ausgeführt werden und nur ein Thread 'io_service :: run() 'aufruft, werden die Socket-Aufrufe in' io_service 'synchronisiert. Boost.Asio erfordert, dass der Anrufer garantiert, dass bestimmte Argumente gültig bleiben, bis der Handler aufgerufen wird. Es zwingt den Benutzer nie, sie auf eine bestimmte Weise zu verwalten. In manchen Fällen kann es einfacher sein, die Objektlebensdauer über Smartpointer der Async-Kette zuzuordnen. –