WSASend: lpNumberOfBytesSent gültiger Wert?



  • Hallo,

    ich habe unter Windows einen non-blocking SOCKET, der iwie problematisch ist. Zum Versenden der Daten im Sendepuffer wird WSASend aufgerufen und der lpNumberOfBytesSent Parameter mit der Anzahl der versendeten Byte befüllt.
    Ist es ein zulässiger Anwendungsfall, dass lpNumberOfBytesSent 0 ist und beim WSASend-Aufruf nichts verschickt worden ist?



  • Hast du den Rückgabewert geprüft, wenn dieser Fall auftritt?



  • @firefly

    Ja, der wird geprüft. Ich weiß gar nicht, ob dieser Fall auftritt, aber die Sendefuntkion kann eigentlich nur in diesem Fall Probleme machen:

    unsigned int TCPConnection::send()
    {
       if( !WriteBuffer_.empty() )
       {
          WriteBuffer_.seek( 0, SeekOrigin::Begin );
          while( WriteBuffer_.available() > 0 )
          {
             int const pos   = WriteBuffer_.tell();
             int const count = WriteBuffer_.available();
             char* data 	 = WriteBuffer_.buffer() + pos;
    
             WSABUF buffer;
             buffer.buf = data;
             buffer.len = min( 1408, count );
    
             DWORD bytes_sent = 0;
             if( ::WSASend( socket(), &buffer, 1, &bytes_sent, 0, nullptr, nullptr ) == SOCKET_ERROR )
             {
                unsigned int const error_code = ::WSAGetLastError();
    	    if( error_code != WSAEWOULDBLOCK )
    	    {
                   WriteBuffer_.erase_front();
                   return error_code;
                }
             }
             if( bytes_sent == 0 )
             {
                ::PostMessage( SyncWindow_, TCPWindowMessages::SendData, socket(), 0 );
                return 0;
             }
    	 WriteBuffer_.skip( bytes_sent );
    
             connection_info().BytesSent 	+= bytes_sent;
             connection_info().LastSendTime = timestamp_current_timestamp();
          }
          WriteBuffer_.erase_front();
       }
       return 0;
    }
    

    Die Zeilen 26-30 sind jetzt für den Fall ergänzt worden. Ansonsten würde der Datenzeiger im WriteBuffer im Falle von lpNumberOfBytesSent = 0 nicht verschoben und die while()-Schleife nicht verlassen.



  • sicher das in dem falle nicht WSAEWOULDBLOCK auftritt?



  • Nein, weiß ich nicht, daher die Frage. Ich dachte bisher, dass lpNumberOfBytesSent > 0 ist oder ein Fehler aufgetreten ist (return wert = SOCKET_ERROR). Ich möchte jetzt wissen, ob lpNumberOfBytesSent 0 sein darf, ohne dass ein Fehler aufgetreten ist. Also ob das Versenden von 0 Bytes als erfolgreich gilt, obwohl man 1+ Bytes versenden möchte.



  • @DocShoe sagte in WSASend: lpNumberOfBytesSent gültiger Wert?:

    Nein, weiß ich nicht, daher die Frage. Ich dachte bisher, dass lpNumberOfBytesSent > 0 ist oder ein Fehler aufgetreten ist (return wert = SOCKET_ERROR). I

    Dein Code ignoriert ja explizit WSAEWOULDBLOCK:

             if( ::WSASend( socket(), &buffer, 1, &bytes_sent, 0, nullptr, nullptr ) == SOCKET_ERROR )
             {
                unsigned int const error_code = ::WSAGetLastError();
    	    if( error_code != WSAEWOULDBLOCK )
    	    {
                   WriteBuffer_.erase_front();
                   return error_code;
                }
             }
    

    Wenn du WSAEWOULDBLOCK bekommst wurde natürlich nichts verschickt, daher ist bytes_sent == 0 nur natürlich. Wobei ich nicht weiss ob es garantiert ist dass der Wert in bytes_sent Sinn macht wenn die Funktion einen Fehler gemeldet hat. Besser wäre bytes_sent im Fehlerfall gar nicht zu prüfen.

    IO auf einem non-blocking Socket kann jederzeit mit WSAEWOULDBLOCK fehlschlagen. Speziell kannst du dich nicht darauf verlassen dass du eine beliebige Menge an Daten verschicken kannst nachdem du per select/... die Notification bekommen hast dass der socket jetzt "bereit zum Schreiben" ist.

    Ich möchte jetzt wissen, ob lpNumberOfBytesSent 0 sein darf, ohne dass ein Fehler aufgetreten ist. Also ob das Versenden von 0 Bytes als erfolgreich gilt, obwohl man 1+ Bytes versenden möchte.

    Nein, das Versenden von 0 Bytes gilt nicht als erfolgreich. Statt dessen bekommst du in dem Fall einen WSAEWOULDBLOCK Fehler. Nur ignoriert dein Code wie schon gesagt explizit WSAEWOULDBLOCK - und "re-interpretiert" WSAEWOULDBLOCK dadurch als "erfolgreich".



  • Ah, danke. Ich bin jetzt auch davon ausgegangen, dass Erfolg bedeutet, dass min. 1 Byte verschickt worden ist.
    Dann müssen wir unser Problem weiter untersuchen, wir haben die Vermutung, dass die while()-Schleife unter bestimmten Bedinungen nicht verlassen wird, und die einzige Erklärung ist, dass das nur mit bytes_sent dauerhaft = 0 auftreten kann.



  • Jetzt verstehe ich gar nichts mehr.

    @DocShoe sagte in WSASend: lpNumberOfBytesSent gültiger Wert?:

    Ich bin jetzt auch davon ausgegangen, dass Erfolg bedeutet, dass min. 1 Byte verschickt worden ist.

    Tut es ja auch.

    Wenn nichts gesendet werden kann weil die Buffer voll sind, dann bekommst du -1 zurück und WSAGetLastError liefert WSAEWOULDBLOCK. Dein Code reagiert dann nur gleich darauf wie wenn WSASend erfolg gemeldet hätte.



  • @hustbaer sagte in WSASend: lpNumberOfBytesSent gültiger Wert?:

    Jetzt verstehe ich gar nichts mehr.

    Warum nicht, was ist unklar?

    Mir ist jetzt jedenfalls klar geworden, warum der ursprüngliche Code ohne die Zeilen 26-30 fehlerhaft war:
    Es gibt drei Fälle, die in einem send- Aufruf auftreten können:

    1. bytes_sent ist nie 0, die Daten werden ggf. in mehreren Schleifendurchläufen komplett versendet.
    2. bytes_sent ist zwischenzeitlich mal 0, das spielt aber keine Rolle, weil in den nächsten Schleifendurchläufen der Rest der Daten versendet wird. Dieser Fall kann auftreten, wenn große Datenmengen versendet werden müssen und die Daten schneller zur Verfügung gestellt werden, als die Schnittstelle sie verschicken kann.
    3. bytes_send bleibt dauerhaft 0, nur in diesem Fall wird die while -Schleife nie verlassen. Und das scheint unser Problemfall zu sein, daher habe ich die Zeilen 26-30 ergänzt. Warum dieser Fall auftritt müssen wir uns jetzt ansehen.

Anmelden zum Antworten