boost::asio::write(), große data-chunks, socket-error 10053



  • Hallo zusammen!
    Seit geraumer Zeit plagt mich bei Verwendung der Boost-Sockets (auf sendender Seite unter Windows) der Socket-Fehler 10053 (Connection Abort). Der Usecase ist einfach der, dass ich nen Windows-Client mit den Boost-Socks habe, mit dem ich große Files (so um einen Megabyte groß) an einen Server schicken will, der auf Linux läuft und der nicht die boost-libs benutzt. Das Problem lässt sich so erklären: Kleine Mengen an Daten (30 bytes, 100 bytes, 5000 bytes) lassen sich prächtig übermitteln. Der Server empfängt in einer recv() schleife alles, während boost::asio::write() zurückgibt, alles gesendet zu haben. Größere Datenmengen bereiten mir da Probleme. boost::asio::write() returnt mit einem 10053er Fehler, ohne alles geschrieben zu haben.

    Habe herausgefunden, dass das wohl etwas damit zu tun haben kann, dass ein Timeout stattfindet, weil eine Seite irgendein ACK braucht oder whatever.

    Habt ihr so etwas ähnliches auch schonmal erleben müssen?

    Freue mich auf Antwort

    Sebastian



  • 1 Megabyte zu übertragen ist riskant. Laut Standard darf wegen der Übertragungsbreite nur ein Paket mit höchstens 1500 Byte übertragen werden.

    Wer zuhause noch ein Holzmodem stehen hat wird dein Programm niemals benutzen können (wie du siehst, nichtmal du selbst).

    🙂



  • @Kóyaánasqatsi:
    Das stimmt so nicht. 1 MB mit TCP zu übertragen ist überhaupt nicht riskant.

    TCP hat diesbezüglich kein Limit, es "fragmentiert" die Daten selbständig.

    Bei UDP finde ich z.T. widersprüchliche Angaben. Einerseits findet man die Info dass IP die UDP-Pakete selbständig fragmentieren sollte, andererseits findet man genauso Hinweise darauf dass man keine UDP Pakete ("Datagrams") > 8KB schicken sollte, weil die dann u.U. nicht ankommen werden. Grösser als 64 KB geht auf keinen Fall, da das Grössenfeld der UDP-Header nur 16 Bit hat.

    @i8087:
    Nö, sowas hatte ich noch nie. Du kannst gerne mal probieren was passiert wenn du statt einem grossen write() mehrere kleine write() verwendest, aber IMO darf das keinen Unterschied machen.



  • hustbaer schrieb:

    @Kóyaánasqatsi:
    Das stimmt so nicht. 1 MB mit TCP zu übertragen ist überhaupt nicht riskant

    Doch, ist sehr wohl riskant: http://de.wikipedia.org/wiki/Maximum_Transmission_Unit

    Klar geht auch mehr, aber 1MB mit einem Batzen rüberzuprügeln ist schon etwas viel, meinst du nicht? 😕



  • *facepalm*

    Nein, meine ich nicht.
    Nochmal: TCP zerlegt das selbständig in Fragmente die kleiner als die MTU des IP-Stacks sind, und übergibt diese Fragmente dann an IP. Der Empfänger bastelt das Ganze wieder zusammen.

    Was soll da Probleme machen?



  • Wie mach man es denn richtig? Zum beispiel die "Großen" - wie machen die das? Stückeln die ihre Messages und schicken sie nach und nach raus? Denn auch ich dachte, wie ihr, dass der TCP-Stack das schon für mich übernehmen würde.



  • Denn auch ich dachte, wie ihr, dass der TCP-Stack das schon für mich übernehmen würde.

    Tut er auch. Wie sieht denn die Client Seite aus? Tritt ev. dort ein Problem mit viel Daten auf (egal ob in Chunks oder als Ganzes)?



  • Also ich bau mir nen std::vektor, bestehend aus unsigned chars zusammen den ich dann senden will. write() wird ein mal mit dem ganzen teil (vektor) aufgerufen. Bei kleinen datenmengen gibts keine probleme (zumindest keine, die ich im LAN feststellen würde), bei großen hingegen tritt der besagte 10053er fehler auf. Der server, an den geschickt wird, hat ein select, mit dem er checkt ob an nem port was anliegt und dann ggf. recv() aufruft. Ich mein, das hat sich doch über jahrzehnte hinweg bewährt, warum gibts da jetzt stress? irgendwas läuft da faul. ich werd nachher mal bisschen betreffenden code posten, vielleicht könnt ihr da was auf anhieb erkennen.



  • Kóyaánasqatsi schrieb:

    hustbaer schrieb:

    @Kóyaánasqatsi:
    Das stimmt so nicht. 1 MB mit TCP zu übertragen ist überhaupt nicht riskant

    Doch, ist sehr wohl riskant: http://de.wikipedia.org/wiki/Maximum_Transmission_Unit

    Klar geht auch mehr, aber 1MB mit einem Batzen rüberzuprügeln ist schon etwas viel, meinst du nicht? 😕

    Du verwechselst das wohl mit der http://de.wikipedia.org/wiki/Maximum_Segment_Size

    Ich würde vermuten der Outgoing-Buffer ist voll.
    Probiers doch einfach mal mit einem boost::asio::asnyc_write().



  • @i8087:
    Ich vermute eher dass das Problem beim Server liegt. Oder kannst du das sicher ausschliessen?

    Und ja: etwas Code von Client & Server würde nicht schaden.

    @nurf:

    Ich würde vermuten der Outgoing-Buffer ist voll.

    Ich sehe nicht wie bei TCP mit der klassischen Sockets API ein "Outgoing-Buffer" voll sein sollte. Bzw. genauer: wie dieser Umstand zu einem Problem führen sollte, abgesehen davon dass irgendwas etwas länger dauert.
    Es kann ja immer der vom Aufrufer übergebene Puffer verwendet werden. Dieser muss ja bis zum Abschluss des send() gültig bleiben (ganz egal ob send() nun synchron oder asynchron läuft).

    Oder meinst du was anderes?



  • Ich verwende in solchen Faellen wireshark und seh mir genau an wo der Fehler auftritt.

    Meistens liegt so ein Fehler am Server, wie hustbaer schon gesagt hat.

    Und TCP kann sehr wohl jeder Art von Daten umgehen. MSS und MTU haben nichts damit zu tun. Die sind fuer den Application Layer naemlich komplett uninteressant. Die sind fuer den Transport der einzelnen TCP Pakete wichtig - aber das ist ein komplett anderer Layer.

    Man kann btw ueber TCP auf ganze Terabyte an Daten uebertragen 😉



  • Shade Of Mine schrieb:

    Man kann btw ueber TCP auf ganze Terabyte an Daten uebertragen 😉

    Da brauchst du aber eine große Porno-Sammlung 🤡



  • /dev/random tuts auch



  • Pr0n möchte ich nicht damit übertragen. Sonst platzen mir die kommunikationspartner weg
    Jedenfalls hier mein write()-Aufruf:

    boost::system::error_code boostTransmissionErrorCode;
    // bytesWritten soll ja dieselbe Größe haben wie localCpy.size()
    // das trifft jedoch eben manchmal nicht zu. Wenn dies nicht zutrifft, wird in boostTransmissionErrorCode der besagte Fehler geschrieben.
    // lässt man boostTransmissionErrorCode weg, wird eine exception geworfen.
    bytesWritten = boost::asio::write(*(socket), boost::asio::buffer(&localCpy[0],localCpy.size()), boost::asio::transfer_all(), boostTransmissionErrorCode); 
    recv();
    

    Auf der Server-Seite sieht das ganze so aus:

    EDIT: Sorry, vergessen den servercode zu pasten

    // hier vielleicht interessant, das select
    		if( ( n = select( fd_skt+1, &rd_set , NULL, NULL, &tv)) > 0 ){
    
    			do{
    				uiRsize = m_TCPCon->recv(&vucRec[0], RECSIZE, 0);
    				// instanzmethode ruft einfach nur auf	
    				//recv(m_TCPCon->m_iSocketDescriptor,buffer,RECSIZE,0);
    
    				sizeCounter += uiRsize;
    
    				switch (uiRsize){
    					case -1: // fehl0r
    						// irrelevanter code
    						break;
    					case 0: // nix
    						// irrelevanter code
    						break;
    					default: // etwas empfangen
    						// schreib das zeug in den ergebnispuffer
    						vucRec.resize(uiRsize);
    						vucData.insert( vucData.end(), vucRec.begin(), vucRec.end() );
    						vucRec.clear();
    				}
    				vucRec.resize(RECSIZE);
    
    				usleep(100); // je hoeher man den krempel stellt, desto mehr wird empfangen.
    
    			} while( uiRsize == RECSIZE); // mach den kram so lange nicht weniger als RECSIZE Bytes empfangen wurden
    
    			sprintf(sizeCounterString, "sizeCounter=%d",sizeCounter);
    			logger.logWarning("Empfangen:",sizeCounterString);
    			this->processInData(vucData);
    


  • Dieser Server ist ja extrem ressourcenschonend!



  • Ja, Fehler liegt im Server. Fehler weil Doku nicht gelesen. DER Klassiker bei der Sockets API schlechthin.

    // } while( uiRsize == RECSIZE); // mach den kram so lange nicht weniger als RECSIZE Bytes empfangen wurden
    // so geht das aber nicht.
    // SO geht das:
     } while(uiRsize > 0); // mach den kram so lange neue daten empfangen wurden
    

    Jetzt hast du natürlich ein anderes Problem, nämlich dass dein Server nicht mehr weiss wann er aufhören darf/kann/muss.

    Das löst du am besten indem du vor dem Datenblock noch die Grösse des Datenblocks überträgst.



  • hustbaer schrieb:

    DER Klassiker bei der Sockets API schlechthin.

    Wieso bei Sockets API?
    Generell bei Streams, nur fällt es anders häufig nicht auf 😉



  • Socket? schrieb:

    hustbaer schrieb:

    DER Klassiker bei der Sockets API schlechthin.

    Wieso bei Sockets API?
    Generell bei Streams, nur fällt es anders häufig nicht auf 😉

    Ich hab einfach die Beobachtung gemacht, dass es Leute bei anderen Streams checken, aber bei der Sockets API nicht.

    Vermutlich deswegen, weil sie glauben dass eine Socket-Verbindung kein "reiner" Stream ist, sondern irgendwie "Paketgrenzen" berücksichtigt und mitüberträgt.

    Und natürlich fällt man bei anderen Streams sofort auf die Schnauze -- wenn man versucht sowas mit einem File auf ner Platte zu machen geht es gleich beim 1. mal nicht, und man weiss sofort dass man ein "Problem hat" (=es eben anders machen muss).

    Bei TCP Streams dagegen kann es überraschend lange funktionieren (immer schön kleine Pakete, so dass nie ein send() zu mehr als einem TCP Paket führt, immer schön Flusswechsel nach jedem Paket - dann kann das "ewig" gutgehen).

    Dann (also wenn jemand das "Funktionieren" dieser "Technik" bereits beobachtet hat, es aber "auf einmal" nicht mehr "funktioniert") kommen übrigens auch die lustigen bzw. nervigen (je nachdem wie man es aufnimmt) Beiträge ala "glaub ich dir nicht, weil das mach ich schon immer so, und das hat bis jetzt immer funktioniert" 😉


Anmelden zum Antworten