[Socket] Clientside - Empfangen "unlimitierter" buffer.



  • Hab ich jetzt nicht getestet, vielleicht funktionert es ja:

    int Socket::recv (std::string& s)
    {
       size_t ntoread =::recv(m_sock, 0, SOMELIMIT, MSG_PEEK);
       char* buf = new char[ntoread];
       s.clear();
       int status = ::recv ( m_sock, buf, ntoread, 0 );
       delete [] buf;
    }
    

    Ist halt teuer, da der Speicher immer wieder neu allokiert wird. Müsste man noch irgend ein Allocator oder Container basteln.

    Am Besten ist es natürlich wenn man die ganzen char-Arrays rausschmeisst, und nur noch STL Strings verwendet:)

    edit: copy&past hat ein Durcheinander veranstaltet 😉 + Beschissene Satzstellung verbessert



  • Muss meine Aussage kurz überprüfe, bin mir nicht mehr ganz sicher.



  • pasti schrieb:

    Die Aussage von Ponto stimmt leider nicht:

    Alternativ kannst du es bei deinem Buffer belassen und diesen nach dem Empfangen nach einem Terminierungszeichen scannen. Den Rest solltest du natürlich behalten, da dies schon die nächste Nachricht sein kann.

    Ein Aufruf von "recv()" liest genau eine oder weniger als eine "Message". Man muss höchtens "recv()" ein 2. Mal ausführen, wenn nicht die ganze "Message" im Netzwerkpuffer Platz hatte. Für ein Bsp. siehe "UNIX Network Programming" Sektion 3.9

    Wir haben es mit zwei verschiedenen Ebenen zu tun. Nachrichten auf der Anwendungsebene und Nachrichten auf der Transportschicht. Diese können beliebig gemischt sein. Eine Nachricht der Anwendung kann sich über mehrere Pakete der Transportschicht erstrecken. Ebenso können mehrere Anwendungsnachrichten im selben Paket landen.

    Zusätzlich kann es passieren, dass bereits eine zweite Anwendungsnachricht beim Programm angekommen ist, ohne dass die erste abgeholt wurde. Dann werden beide gleichzeitig ausgeliefert.

    Ich kann weder aus der Beschreibung in 3.9 noch 13.3 im UNP oder der manpage von recv rauslesen, dass sich diese Funktion an Paketgrenzen hält.

    In der Section 3.9 haben wir auch noch ein readline von Stevens, dass einen static buffer benutzt um die alten Bytes nicht zu verlieren.



  • am besten ist es die laenge der zu uebertragenden bytes mitzuschicken.

    wenn du das nicht machen kannst, kannst du ja blockweise empfangen
    und an einen string haengen (byteweise empfangen ist sehr schlecht).

    viele andere moeglichkeiten werden dir da nicht bleiben.



  • @ponto:

    Ich weiss nicht, vielleicht unterliegen wir beide auch nur einem Missverständniss. Aber ich habe mal ein kleines Bsp. Prog. geschrieben

    "CLIENT"
    
    int main(int argc, char *argv[])
    {
     int m_sock = socket(AF_INET, SOCK_STREAM, 0);
     sockaddr_in addr;
     addr.sin_family = AF_INET;
    
     addr.sin_port = htons(12349);
     inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
    
     connect(m_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
    
     char buf[100] ="hallo";
     char buf2[100] ="gurke";
    
     send(m_sock,buf, 100,MSG_NOSIGNAL); 
     send(m_sock,buf2, 100,MSG_NOSIGNAL); 
    
     return EXIT_SUCCESS;
    }
    
    und
    
    "SERVER"
    
    int main(int argc, char *argv[])
    {
     int m_sock = socket(AF_INET, SOCK_STREAM, 0);
     sockaddr_in addr;
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = htonl(INADDR_ANY);
     addr.sin_port = htons(12349);
    
     bind(m_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
     listen(m_sock, 5);
     int con = accept(m_sock,0,0);
     char buf[100];
     int len;
    
      sleep(5);
      len = read(con,buf,100);
      cout << len << "  :" << buf << endl;
      len = read(con,buf,100);
      cout << len << "  :" << buf << endl;
      return EXIT_SUCCESS;
    }
    

    Wenn man beide Programme startet ( zuerst den Server und gleich danach den Client ) steht bei mir auf der Konsole:

    100 :hallo
    100 :gurke

    -> 1. read gibt 1. send aus, 2. read gibt 2. send aus, obwohl zum Zeitpunkt des 1. read bereits beide send ausgeführt waren.

    Oder ist mir irgend ein dämlicher Fehler unterlaufen?

    edit: Habe das ganze ein wenig mehr "C++finiziert". :p



  • Wahrscheinlich hast du recht. Es verwundert mich aber schon, dass der Kernel die Pakete beim Server nicht zusammenlegt.

    DIm Posix Standard steht einerseits folgendes:

    "For stream-based sockets, such as SOCK_STREAM, message boundaries shall be ignored."

    Andererseits steht da auch:

    "If the MSG_WAITALL flag is not set, data shall be returned only up to the end of the first message."



  • Wenn diese read <-> send Beziehung nicht existieren würde, würden auf einen Schlag einige meiner Programme nicht mehr funktionieren 😮

    Z.B. Wenn ein Client einzelne Kommandos an den Server liefert, wäre es schwierig die ganzen Daten auseinander zu pflücken. So funktionert das "Out-of-the-Box". 🕶



  • Die Beziehung besteht tatsächlich nicht. Ich habe folgendes übersehen:

    send(m_sock,buf, 100,MSG_NOSIGNAL); 
     send(m_sock,buf2, 100,MSG_NOSIGNAL);
    

    Wenn du immer 100 Bytes schreibst und 100 Bytes liest, wird es nicht schiefgehen. Dies Strings bestehen aber nur aus 6 Bytes. Probier es mit:

    send(m_sock,buf, 6,MSG_NOSIGNAL); 
     send(m_sock,buf2, 6,MSG_NOSIGNAL);
    

    Alle Pakete auf die gleiche Länge festzulegen, wäre eine Möglichkeit damit umzugehen, dass TCP nur einen Stream liefert und keine einzelnen Messages. Zwei weitere Möglichkeiten sind das senden der Länge vor der Nachricht und das Einfügen eines Nachrichtentrenners.

    Deine Anwendungen sollten aber nicht davon ausgehen, dass ein read sich an Paketgrenzen hält.



  • Ihr habt da was falsch verstanden.

    Der Server/Client ließt die Daten aus dem Kernel. Dort besteht ein Speicher für diesen Socket.

    Schickt der Client/Server nun mehrere Nachrichten ohne das der Server/Client sie abholt dann sind sie solange im Speicher bis der Socket schließt oder der C/S sie abholt.

    Wieviel man aus diesem Kernelspeicher (Socket) holt bleibt euch überlassen. Man bekommt mir read die Anzahl und wen keine Daten da sind auch dies mitgeteilt.

    Man darf sich auch nicht darauf verlassen das bei einem Send der Read auch die ließt. Wenn inerehalb einer bestimmten Zeit ein 2ter Sedn gemacht wird, dann wird das ganez in ein Packet verpackt und nur einmal an den Server übermittelt.
    Gibt sogar einen Ausruck dafür der mit ejtzt nicht einfällt. Auch die ms. zum neuen Read weiß ich nicht mehr (zu lange her).
    Da man in dem Fall aber sowieso eine Protokollsprache haben sollte ist es eigentlich egal.

    Für Anfänger jedoch etwas verwierend wenn man zuerst immer Zeile für Zeile bekommt und auf einmal geht es nicht mehr.



  • nagle



  • @Unix-Tom

    Ok, you're right. Das ganze wird wirklich zusammengefügt.Bin jetzt schlauer 😃


Anmelden zum Antworten