2011-01-11 13 views
33

Natürlich wird BeginReceive() nie enden, wenn keine Daten vorhanden sind. MSDN suggests dieser Aufruf Close() würde BeginReceive() abbrechen.Wie Socket BeginReceive() abbrechen?

jedoch Close() auf dem Sockel Aufruf führt auch ein Dispose() auf sich, wie in this great ansewr herausgefunden, und folglich EndReceive() würde eine Ausnahme aus, da das Objekt bereits angeordnet ist (und es tut!).

Wie soll ich fortfahren?

+0

http://stackoverflow.com/questions/1921611/c-how-do-i-terminate-a-socket-before-socket-beginreceive-calls-back – SwDevMan81

Antwort

40

Es scheint, als ob dies (durch das sehr dumme) Design ist. Diese Ausnahme muss in Ihrem Code ausgelöst und abgefangen werden.

MSDN sieht darüber zwar still, aber wenn man sich die Dokumentation einer anderen asynchronen Socketmethode suchen, BeginConnect(), hier ist, was wir finden:

einen anstehenden Anruf an die Beginconnect() -Methode zum Abbrechen Schließen Sie den Socket. Wenn die Close() - Methode aufgerufen wird, während ein asynchroner Vorgang ausgeführt wird, wird der Callback der BeginConnect() - Methode aufgerufen. Ein nachfolgender Aufruf der EndConnect (IAsyncResult) -Methode wird eine ObjectDisposedException auf werfen, um anzuzeigen, dass die Operation abgebrochen wurde.

Wenn es die richtige Vorgehensweise für BeginConnect ist, ist es wahrscheinlich auch für BeginReceive. Dies ist sicherlich ein schlechtes Design seitens der Microsoft-async-API, da es den Debugger nerven würde, den Benutzer dazu zu bringen, eine Exception als Teil eines normalen Flusses zu werfen und abzufangen. Sie haben wirklich keine Möglichkeit zu warten, bis der Vorgang abgeschlossen ist, denn Close() ist, was es an erster Stelle vervollständigt.

+4

Ich kann nicht glauben, dass es gebaut wurde, um so zu arbeiten, aber es ist, was es ist, nehme ich an. – Kelly

+0

@Kelly: Was ist daran falsch als Design? Code, der 'EndReceive' aufruft, muss Fehler behandeln, unabhängig davon, ob eine Ausnahme ausgelöst wird oder nicht. Wenn die Bedingung direkt am "EndReceive" behandelt werden kann, würde es keine "Ausnahme" zulassen, wenn ein "catch" durch ein "if" ersetzt wird, aber wenn es dann nicht behandelt werden kann, müsste man 'hinzufügen wenn ([es gescheitert]) [etwas] werfen würde und * noch * einen Fang brauchen würde. Verbindungen sollten nicht oft genug abgebrochen werden, um den Leistungsaufwand bei der Verwendung einer Ausnahme gegenüber einem "If" zu minimieren. – supercat

+4

Da das Werfen und Erfassen einer Ausnahme als Teil eines normalen Inputs im Gegensatz zu einem "außergewöhnlichen" Flow ein schlechtes Design ist, unabhängig von den Auswirkungen auf die Performance. Es kann auch das Debugging stören, was oft nützlich ist. –

-1

Eine andere Lösung wäre, "sich selbst" eine "Steuernachricht" zu senden, die einen Socket verwendet, der an einen anderen Port gebunden ist. Es ist nicht genau ein Abbruch, aber es würde Ihre asynchrone Operation beenden.

+0

Sie könnten auch einen Wartepunkt oder ein Ereignis verwenden. Sie könnten auch Optionen verwenden, wie ich in meiner Antwort angegeben habe. Dies erfordert einen weiteren Socket pro Client und Server. – Jay

-1

Ich hatte auch mit diesem Problem zu kämpfen, aber soweit ich das mit einem einfachen booleschen Flag feststellen kann, bevor das Aufrufen von .BeginReceive() funktioniert auch (so dass es keine Notwendigkeit für die Ausnahmebehandlung gibt). Da ich bereits die Handhabung von Start/Stopp hatte, handelte es sich bei diesem Fix um eine if-Anweisung (scrollen Sie bis zum Ende der OnReceive()-Methode).

if (_running) 
{ 
    _mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null); 
}     

Sollte ich etwas mit diesem Ansatz übersehen haben, lassen Sie es mich wissen!

+0

Dies funktioniert nicht, wenn er NACH dem Aufruf von BeginReceive abbrechen möchte. Wenn BeginReceive einen asynchronen Rückruf erstellt, wird dieser asynchrone Rückruf warten, bis Daten empfangen werden. Was, wenn er nach dem Aufruf von BeginReceive abbrechen möchte, aber bevor der Async-Rückruf ausgeführt wird (was ist seine Frage?)? Ihre Lösung würde in diesem Fall nicht funktionieren. –

+0

Daher ist im OP-Fall eine Ausnahmebehandlung erforderlich, da der asynchrone Rückruf nur abgebrochen werden kann, indem der Socket geschlossen wird. Dadurch wird eine ObjectDisposedException in EndReceive ausgelöst. –

2

Ich bin überrascht, dass niemand SocketOptions empfohlen.

Sobald der Stapel den Sende- oder Empfangsvorgang hat, ist er an die Socket-Optionen des Sockets gebunden.

Verwenden Sie ein kleines Sende- oder Empfangs-Timeout und verwenden Sie es vor dem Vorgang, so dass es Ihnen egal ist, ob es während desselben Vorgangs zu etwas kürzer oder länger geändert wird.

Dies führt zu mehr Kontextwechsel, erfordert jedoch nicht das Schließen des Sockets unter einem Protokoll.

Zum Beispiel:

1) Stellen Sie eine kleine Zeitüberschreitungs

2) Führen Operationen

3) Stellen Timeout größer

Dies ist vergleichbar mit der Verwendung Blocking = false aber mit einem automatischen Zeitlimit, das Sie angeben.

+1

Die Sende-/Empfangs-Timeout-Optionen für Sockets gelten nur, während eine blockierende E/A-Operation ausgeführt wird. Ich habe nicht untersucht, wie die asynchrone I/O-Implementierung des Frameworks implementiert wird, aber ich hoffe, dass dies mit einem tatsächlichen asynchronen Mechanismus wie WSASelect- oder I/O-Completion-Ports geschieht und nicht mit einem Thread, der in einem Blockiervorgang sitzt. –

0

Für TCP-Socket-Verbindungen können Sie die Connected Eigenschaft verwenden, um den Zustand der Steckdose, um zu bestimmen, bevor sie zugreifen möchten irgendwelche entsorgten Methoden. Per MSDN:

"Die Connected-Eigenschaft ruft den Verbindungsstatus des Sockets ab der letzten E/A-Operation ab. Wenn sie false zurückgibt, war der Socket entweder nie verbunden oder ist nicht mehr verbunden."

Da es "nicht mehr verbunden" bedeutet, bedeutet dies, dass zuvor eine Close() auf dem Socket aufgerufen wurde. Wenn Sie zu Beginn des Empfangsrückrufs prüfen, ob der Socket verbunden ist, gibt es keine Ausnahme.

+1

Sie erkennen, dass diese asynchronen Sockets in einem Multithread-Programm sind, richtig? Sie können auf einer Zeile in Thread A überprüfen und die Ausführung kann dann in Thread B und dann wieder in Thread A schließen, Ihre Bedingung ist aus dem Fenster. –

Verwandte Themen