Protokoll Übertragung
-
Hallo,
ich versuche ein simples Protokoll zu erstellen und in dem Format soll eine Datei über eine Socketverbindung übertragen werden:
CODE | SIZEOFDATA | DATA
CODE ist eine zweistellige Zahl, z.B. 16
SIZEOFDATA ist eine Zahl mit einer maximalen Größe, z.B. 65535Wie wähle ich nun am besten die Datentypen für diese Variablen und wie baue ich die Variablen zu einer zusammen, um sie dann zu übertragen?
Etwas Pseudocode wär sehr nett.
Danke.
-
Ich würde für CODE einen char (8 Bit Integer, viel kleinere Datentypen kennt C++ nicht), für SIZE einen unsigned short (16 Bit) und für DATA einen std::string verwenden (wobei, SIZE würde ich intern gar nicht speichern, sondern aus der Länge des Strings berechnen, wenn nötig).
Zum Schreiben auf den Socket kannst du CODE und SIZE zu einem 3-Byte-Block zusammenfassen (reinterpret_cast) und vor den String schreiben - anschließend schickst du die Daten durch die Leitung.
char head[4]; head[0]=CODE; reinterpret_cast<unsigned short*>(head+1) = DATA.length(); head[3]=0; send((head+DATA).c_str(),DATA.length()+3);Beim Lesen holst du dir erst die beiden Header-Elemente und danach die Daten:
recv(&CODE,1); unsigned short SIZE; recv(&SIZE,2); DATA.resize(SIZE); recv(&DATA[0],SIZE);
-
Ein paar Hinweise hätte ich dazu.
reinterpret_cast<unsigned short*>(head+1) = DATA.length();führt bei manchen Plattformen (Sparc beispielsweise) höchstwahrscheinlich zu einen Fehler, da Sparc keine ungeraden Speicheradressen mit 16 Bit ansprechen kann. Besser ist, die 2 Bytes einzeln zu setzen und auf der anderen Seite auch wieder zusammensetzen. Wenn es über das Netzwerk geht, könnte ja sogar eine andere CPU mit einem anderen Endianess vorliegen, was zu vertauschten high- und low-Bytes führt. Also etwa so:
Auf Senderseite:
head[1] = (DATA.length() >> 8) & 0xff; // high-byte head[2] = DATA.length() & 0xff; // low-byteAuf Empfängerseite:
unsigned short size = (head[1] << 8) | head[2];Ausserdem würde ich sowohl das Versenden noch das Empfangen über einen Puffer machen. send und recv sind Systemaufrufe, die recht teuer sind. Ein Senden oder Empfangen eines einzelnen Bytes mit solch einem Systemaufruf ist ein Performancekiller.
Tntnet
-
tntnet schrieb:
Ein paar Hinweise hätte ich dazu.
[...]Ich hab' nie beauptet, daß es perfekt ist - das waren, wie deetee auch gebeten hat, lediglich einige Pseudocode-Fetzen

-
hi,
der ansatz ist denke ich auf jedenfall das was ich brauche.
Noch ne Frage zu Big und Little Endian. Brauche ich auf senderseite für meinen Fall nun htons/htonl und für den empfang ntohs/ntohl? Jedenfalls für CODE und SIZE oder?
ok, dann versuch ich mich mal daran. Vielen dank für eure Beteiligung.
-
hton... (host-to-network...) verwendest Du auf Senderseite und ntoh... (network-to-host... auf Empfängerseite. Oder Du machst es so, wie ich geschrieben habe mit Bitoperationen. Die sind von Endianess unabhängig.
Tntnet
-
ok, ich bekomms nicht hin. Ich glaube mit meinem Speicher buf stimmt was nicht, hab ich den richtig deklariert von der Speichergröße her?
try { if(f.is_open()) { //char* buf = new char[SIZE_CODE2 + sizeof(long) + SIZE_BUF_FILE]; // block format char* data = new char[SIZE_BUF_FILE]; // pure file content char* buf = new char[3]; char* ptr; long bufSize = 0; unsigned char code; unsigned short size; f.seekg(0); // file beginning position while(packages > 0) { f.read(data, SIZE_BUF_FILE); bufSize += f.gcount(); // currently read signs size = f.gcount(); if(f.eof()) {// send last or maybe only package code = CODE_LASTBLOCK; ptr = (char *) &code; buf[0] = ptr[0]; ptr = (char *) &size; buf[1] = ptr[0]; ptr = data; buf[2] = ptr[0]; //sprintf(buf, "%d\n%ld\n%s", code, size, data); cout << "LAST " << endl; //Wrapper::Send(dataSock, buf,SIZE_CODE2 + sizeof(long) + size+1 + 2, 0); Wrapper::Send(dataSock, buf, size+3, 0); } else if(bufSize == SIZE_BUF_FILE) { bufSize = 0; // reset bufSize code = CODE_MOREBLOCKS; ptr = (char *) &code; buf[0] = ptr[0]; ptr = (char *) &size; buf[1] = ptr[0]; ptr = data; buf[2] = ptr[0]; //sprintf(buf, "%d\n%ld\n%s", code, size, data); cout << "MORE " << endl; //Wrapper::Send(dataSock, buf, SIZE_CODE2 + sizeof(long) + SIZE_BUF_FILE + 2, 0); Wrapper::Send(dataSock, buf, size+3, 0); } packages--; } //delete[] buf; //delete[] data; cout << "get nach while()" << endl; //f.close(); cout << "get nach f.close()" << endl; } } catch(std::bad_alloc & exc) { cerr << exc.what() << endl; }
-
Zumindest für den Fall, daß du noch Daten übergeben willst, ist der Buffer zu klein. Außerdem kannst du auch Arrays verwenden, wenn die benötigte Größe zur Laufzeit feststeht:
unsigned char buf[3+SIZE_BUF_FILE]; unsigned char code,*data=buf+3;//die ersten drei Bytes des Puffers sind der Header unsigned short size; while(pacckages>0) { f.read(data,SIZE_BUF_FILE); size=f.gcount(); code=(f.eof())?CODE_LASTBLOCK:CODE_MOREBLOCKS; buf[0]=code; buf[1]=size>>8; buf[2]=size& 0xFF; Wrapper::Send(dataSock,size+3,0); --pacckages }PS: Und wenn Code fast identisch wiederholt wird, lohnt sich eine Umstrukturierung des Programms

-
ok, jetzt hab ichs. Stimmt, man kann da noch optimieren, werde ich dann auch mal machen.
try { if(f.is_open()) { char* ptr; // tmp pointer unsigned char code; unsigned short size; char* buf = new char[sizeof(code)+sizeof(size)+SIZE_BUF_FILE]; char* data = new char[SIZE_BUF_FILE]; // pure file content f.seekg(0); // file beginning position while(packages > 0) { f.read(data, SIZE_BUF_FILE); size = f.gcount(); if(f.eof()) {// send last or maybe only package code = CODE_LASTBLOCK; ptr = (char *) &code; buf[0] = ptr[0]; ptr = (char *) &size; buf[1] = ptr[0]; for(int i = 0; i < size; i++) { buf[2+i] = data[i]; // write data char by char into buf } Wrapper::Send(dataSock, buf, size+sizeof(char)*2, 0); } else if(size == SIZE_BUF_FILE) { code = CODE_MOREBLOCKS; ptr = (char *) &code; buf[0] = ptr[0]; ptr = (char *) &size; buf[1] = ptr[0]; for(int i = 0; i < size; i++) { buf[2+i] = data[i]; // write data char by char into buf } Wrapper::Send(dataSock, buf, size+sizeof(char)*2, 0); } packages--; } delete[] buf; delete[] data; f.close(); } } catch(std::bad_alloc & exc) { cerr << exc.what() << endl; }