[C++] Netzwerk

[Moepi]1

Lt. Commander
Registriert
Jan. 2002
Beiträge
1.233
So, das Socketprinzip hab ich jetzt kapiert denke ich. Allerdings kann ich derzeit nur Strings (Char Arrays) übers LAN jagen. Das sieht im Code etwa so aus:

Server:
Code:
char send_buffer[] = "Message to send";
int bytes;
bytes = send(client_socket, send_buffer, strlen(send_buffer) );

Client:
Code:
char rec_buffer[1024];
int bytes;
bytes = recv(client_socket, rec_buffer, sizeof(rec_buffer) - 1, 0);
if(bytes != -1)
  {
     rec_buffer[bytes] = '\0';
     printf("%s",rec_buffer);
  }

Das funktioniert jetzt auch ganz nett, aber ich will eigentlich keinen Text sondern Binärdaten verschicken (einmal Integerwerte und zum anderen Bildmaterial). Meine Frage ist jetzt, wie ich sowas am sinnigsten mache.
Am Client ist der Char Buffer ja im Prinzip schonmal gut. Wenn wir jetzt aber von unserem Integerbeispiel ausgehen, müsste der Client via atoi einen String vom Server in nen Integer wieder zurückverwandeln können.
Wie verpack ich aber am Server am besten nen Integer als String? Mit itoa, ok - Länge? 32bit - dacht ich auch, klappt aber leider net. Wenn ich die Werte so hin und her konvertiere bekommt der Client zwar 4 Bytes rein, allerdings funktioniert die Rückwandlung des Strings in Integer nicht mehr.

Ihr seht ich bin da etwas am Basteln. Hat jemand ein par Tips für mich, wie man das zu machen hat? Oder läuft es wirklich auf ein hin und herwandeln von Strings in Integer oder weiß Gott was hinaus?
 
Kurze Frage, lange Antwort :D



Zuerstmal kann man die Integers auch im Binärformat übers Netz jagen.
Soweit sogut. Jetzt kommt das aber.

Um das ganze vernünftig und portabel zu machen, muss man die Integers in Network-Byte-Order konvertieren und danach wieder in Host-Byte-Order. (An dieser Stelle solltest du dich über Little-/Big-Endian-Darstellung informieren - würde sonst viel Tipparbeit)
Das passiert mit den Funktionen (auf manchen Plattformen auch Makros) htonl und ntohl. Diese wandeln 4-Byte-Zahlen um. Die Funktionen gibt es auch nochmal für 2-Byte-Zahlen, nämlich htons und ntohs.

Damit kommen wir zum nächsten Problem: Niemand garantiert dir, dass int 4 Byte groß ist. Wenn ich den Code hier auf Arbeit kompilieren würde, hätte int 8 Byte. Und auf jedem Rechner mit 64-Bit-Unterstützung kann dir das auch passieren.
Also prüf zumindest beim Programmstart die Größe von int mit sizeof ab.


Willst du die Ints als Strings verschicken, musst du anders vorgehen. Zuerst: itoa ist keine Standard-C/C++ Funktion (atoi dagegen schon). Dafür würde man sprintf nehmen (Daas wäre der C-Weg). Oder auf dem C++-Weg einen Stringstream.


Um einen String rüberzuschicken, musst du auf der anderen Seite wissen, wann er zuende ist. Sprich - du musst die Länge mit reinkodieren. Schick am Anfang am besten ein Längenbyte mit (geht aber nur, falls die Strings max. 255 Zeichen lang werden).


Nur so am Rande: Bist du dir darüber bewusst, dass TCP streambasiert ist? Soetwas wie Pakete gibt es auf der Ebene nicht. Das merkst du ganz schnell, wenn du anfängst viele Daten in hoher Geschwindigkeit zu verschicken.
 
Ok, htons un ntohs hab ich natürlich wieder verpeilt - das dürfte das verquere Ergebnis von -12738957 (oder so ähnlich) erklären, als ich 1024 rübergeschickt hab. Soll jetzt aber auch erstmal egal sein. Problem ist halt, dass mehrere unterschiedliche Werte verschickt werden - mal Integer, mal Long mal String und mal größere Sachen.

Ich hab schon daran gedacht, Kontrollinformationen (Integers usw.) in structs zu verpacken. Deren Länge wäre auch bekannt und ich könnte sie als ein "Paket" (extra in Anführungsstriche) rüberschicken. Dann bin ich aber wieder an dem Punkt, dass ich net weiß, welche Formate ich den Puffern geben soll und wie ich dann beim Empfänger meinetwegen aus nem Char Buffer wieder mein struct zurückgewinne... *grübel*

Dass es da auch schon wieder kein Rezept nach dem Motto "one size fits all" gibt... *grml*



/Edit:
Also das verschicken von Integern klappt jetzt auch - ich verwende halt als Puffer dann einfach den Typ int und umgehe damit die hin und herkonvertiererei. Allerdings versteh ich eins nicht:
Am Server mach ich ein int value = htons(7328)
Jetzt müsst ich doch aber am Client eigentlich ein ntohs(value) machen, nachdem die Daten in value drin sind oder?
Apropro Short und Integer - bei großen Zahlen geht das doch immo schief mit htons... *verzweifel*
 
Zuletzt bearbeitet:
Try this: (geht von 4-Byte Integers aus)

Code:
// senden
int i = 12345;
i = htonl( i);

char[4] buffer;
memcpy( buffer, &i, 4);
send( fd, buffer, 4, 0);



// empfangen
char[4] buffer;
int i;
ssize_t s = recv( fd, buffer, 4, 0);
if( s == 4) {
   memcpy( &i, buffer, 4);
   i = ntohl( i);
}
else {
   // zuwenig daten
}
 
Soweit so gut, aber jetzt hab ich noch was anderes am Hals:

QT verwendet bekanntlich das SIGNAL/SLOT Prinzip, bei dem es die Funktion connect() gibt, um Signale mit Slots zu verbinden. Dummerweise gibts ja aber auch connect() bei der Socketprogrammierung...
Ergebnis: Ich habe 2 Funktionen mit demselben Namen -> Compiler beschwert sich zu Recht - wie kann ich das wieder fixen?
 
Qt connect ist ein Makro. D.h. das ersetzt dir ganz dreist das connect der Socketbibliothek, und zwar bevor der Compiler das zu Gesicht bekommt.

Beste Lösung wäre wohl, den Netzwerkcode vom GUI-Code zu separieren.
 
oder du benutzt die qsocket-klasse. das ist auf jeden fall angenehmer als die socketbibliothek direkt zu benutzen. vor allem muss du dann keine zweite event-loop extra für die netzwerkereignisse bauen und du kannst qdatastream zur datenübertragung nutzen. beides erleichtert die arbeit sehr.
 
Fürs erste hab ich einfach die Includes des ganzen Netzwerkzeugs in nen eigenen Namespace gesteckt - funzt soweit.
Das QT Programm wird sowieso nochmal neu programmiert werden befürchte ich. Mit den QT Funktionen fürs Netzwerk wollte ich mich auch noch befassen - morgen ;)

Danke Euch!
 
Ich würde Dir noch die select() Funktion ans Herz legen.

Damit kannst Du vermeiden das der Code blockiert und darauf wartet das Daten verfügbar sind.

Man kann select() in beiden Fällen verwenden, zum einen um zu prüfen ob der socket beschreibbar ist und zum anderen vor dem lesen um zu prüfen ob Daten vorhanden sind.

MfG

Arnd
 
Zurück
Oben