Schreiben über einen Socket

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Schreiben über einen Socket

Beitrag von Columbus » 18.06.2006 18:13:19

Hallo zusammen,
ich habe mal wieder ein C-Problem. Und zwar möchte ich ganz elementar String über einen Socket übertragen. Dazu habe ich einen Sender und einen Empfänger-Prozess geschrieben. Das Problem ist aber die Daten kommen unvollständig und zerhackt an. Dabei habe ich diesmal genug Platz im Puffer gelassen.

Erst mal der Sender:

Code: Alles auswählen

....
char puffer [ MAX_ZEICHEN ];
      
      struct sockaddr_in mysock;
      struct in_addr     insock;
      .... 
      // socket, connect usw.

strcpy ( puffer, "**END**" );
  
  laenge_string = strlen ( puffer );
  
  if ( ( write ( client_socket, puffer, laenge_string ) ) != laenge_string )
  {
    perror ( "Fehler beim beschreiben des Sockets Ende\n" );
    exit ( errno );
  }
  ...
Was geschickt wird ist also bekannt. Ein String aus 7 Chars und einem Null-Byte.

Nun der Empfänger:

Code: Alles auswählen

...
char *puffer;
char *endzeichen = "**END**";
...
size_t  adr_laenge   = sizeof ( struct sockaddr_in );
      
      struct sockaddr_in mysock;
      struct in_addr     insock;
....
// socket, bind, listen, accept usw.

while ( 1 )
  {
  
    puffer = ( char* ) malloc ( 16 );
    
    if ( ( read ( rueck_gabe, puffer, sizeof ( puffer ) ) ) == -1 )
    {
      perror ( "Fehler beim Lesen aus dem Socket\n" );
      exit ( errno );
    }
  
    write ( STDOUT_FILENO, puffer, 8 );
  
    printf ( "\n" );
    
    free ( puffer );
    
    if ( ! strcmp ( puffer, endzeichen ) )
    {
       break;
    }
    sleep ( 1 );
    
  }
Was dabei rauskommt sieht in etwa so aus:
**EN
D**


...
und wird natürlich nie beendet. Ich habe es auch schon mit einem kleineren Puffer probiert, und auch mit send und recv. Immer das gleiche Problem.
Ausser der Tatsache, daß beim Einsatz von send und recv ich mit nur einer Nachricht durchaus den String richtig übergeben konnte und der Empfänger auch terminierte. Wenn ich aber mehrere Nachrichten übertragen habe, wurden diese auch zerhackt.

Wo liegt das Problem?

Vielen Dank schon mal...

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 19:39:40

Hi Columbus,

Beim Empfänger hast "puffer" als "char *puffer" definiert und in der "read" Funktion beschränkst du die Anzahl der Bytes die du lesen möchtest auf "sizeof(puffer)".

"sizeof(puffer)" entspricht daher der Größe eines Pointers und ist also auf einem 32bit System 4 Bytes lang. Die Ausgabe ist daher völlig korrekt :)
"**EN" sind die ersten 4 Bytes
"D**" ist der Rest

Gruß
gms

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 20:15:31

Ahhh hallo GMS, auf Deine Antwort habe ich gehofft. Ich hatte schon das Wetter in Wien überprüft ( sitzt er bei diesem Wetter drausen beim Heurigen ? ) 8)

Ohh
"sizeof(puffer)" entspricht daher der Größe eines Pointers und ist also auf einem 32bit System 4 Bytes lang.
darauf bin ich mal wieder nicht gekommen. :oops:

Ich habe den Code folgendermaßen geändert:

Code: Alles auswählen

while ( 1 )
  {
  
    puffer = ( char* ) malloc ( 8 );
    
    if ( ( read ( rueck_gabe, puffer, /*sizeof ( puffer ) */ 8 ) ) == -1 )
    {
      perror ( "Fehler beim Lesen aus dem Socket\n" );
      exit ( errno );
    }
  
    write ( STDOUT_FILENO, puffer, 8 );
  
    printf ( "\n" );
    
    if ( ! strcmp ( puffer, endzeichen ) )
    {
       break;
    }
    sleep ( 1 );
    
    free ( puffer );
    
  }
Ganz unelegant, ohne Präprozessor-Anweisung erst mal

Damit wird das "**END**" richtig übertragen.
Aber, wenn ich zuvor noch ein anderes Zeichen übergebe, also mehrere Strings hintereinander, passt noch nicht.

Also ich sende ihm zunächst "**LAN**" und dann "**END**" beide Strings haben die gleiche Länge!

Der Output:
**LAN***
*END****
****
****
....usw
Dabei habe ich das jetzt mit malloc ... ah ich veruche es mit calloc... sieht schon besser aus, aber auch nicht richtig...
**LAN***
*END**

Was könnte das sein?

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 20:41:57

Columbus hat geschrieben:Ich hatte schon das Wetter in Wien überprüft ( sitzt er bei diesem Wetter drausen beim Heurigen ? ) 8)
sollte ich wiedereinmal :), war heute zum Grillen bei meinen Schwiegereltern, auch nicht schlecht
Columbus hat geschrieben: Dabei habe ich das jetzt mit malloc ... ah ich veruche es mit calloc... sieht schon besser aus, aber auch nicht richtig...
Momentan schreibst du immer 8 Bytes auf stdout, egal wieviele du empfangen hast, es kann also theoretisch passieren, daß du Müll rausschreibst.
ein Änderungsvorschlag:

Code: Alles auswählen

 if ( ( readlen = read ( rueck_gabe, puffer, /*sizeof ( puffer ) */ 8 ) ) == -1 ) 
    { 
      perror ( "Fehler beim Lesen aus dem Socket\n" ); 
      exit ( errno ); 
    }
 write ( STDOUT_FILENO, puffer, readlen );
Auf der anderen Seite ist die letzte Ausgabe ja eigentlich korrekt, ist ja nur "unerwartet" formatiert

Code: Alles auswählen

**LAN*** 
*END**
Gruß
gms

[edit]
probiere einmal "send/recv" statt "read/write", dann dürfte dieses Problem eigentlich nicht auftreten
[/edit]
Zuletzt geändert von gms am 18.06.2006 20:50:59, insgesamt 1-mal geändert.

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 20:48:55

..war heute zum Grillen bei meinen Schwiegereltern, auch nicht schlecht
ups!? ;-)

Ja, er müsste aber mit "**END**" aussteigen.

Code: Alles auswählen

char *endzeichen = "**END**"; 
...
if ( ! strcmp ( puffer, endzeichen ) )
    {
       break;
    }
    sleep ( 1 ); 
    free ( puffer ); 
}
Was ja auch funktioniert, wenn ich den "**END**"-String als allererstes übergebe!

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 20:53:04

Du schreibst in Summe "**LAN****END**" und erhältst in Summe "**LAN****END**", oder ?

Aber probiere einmal die Funktionen "send" und "recv", statt "read" und "write".

Benutzeravatar
meandtheshell
Beiträge: 4054
Registriert: 14.01.2005 17:51:30

Beitrag von meandtheshell » 18.06.2006 21:00:05

Eine Anmerkung kosmetischer Natur

Code: Alles auswählen

puffer = ( char* ) malloc ( 16 );

ist IMHO schlechter als eine Kombination aus

Code: Alles auswählen

#define BUFFERSIZE 16
[ ... ]
char *puffer = malloc(BUFFERSIZE  *  sizeof(char));
Die zweite Variante halte ich für guten Programmierstil, die erste nicht.

markus

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 21:03:32

gms hat geschrieben:Du schreibst in Summe "**LAN****END**" und erhältst in Summe "**LAN****END**", oder ?

Aber probiere einmal die Funktionen "send" und "recv", statt "read" und "write".
Ja, das ist richtig, von der Summer her stimmts. Mit send und recv kommt das gleiche raus.
:roll:

Ist mir wirklich schleierhaft.

@meandtheshell: Ja ich weis, das war auch erst mal zum probieren. Habe ich ja auch geschrieben:
Ganz unelegant, ohne Präprozessor-Anweisung erst mal


Gruss Christian
Zuletzt geändert von Columbus am 18.06.2006 21:08:27, insgesamt 1-mal geändert.
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 21:07:12

poste bitte den kompletten Code auf NoPaste.
Ich habe das selber bei mir ausprogrammiert, und da tritt dieses Problem nicht auf. Könnte ich dir auch posten, wenn du möchtest

Gruß
gms

[edit]
Muß mich berichtigen, ich habe hier das gleiche Verhalten, bin auch immer noch der Meinung, daß dieses völlig korrekt ist :wink:
Wenn du die Messages trennen möchtest, könntest du das über einen eigenen Delimiter, oder über fixe Länge bewerkstelligen
[/edit]
Zuletzt geändert von gms am 18.06.2006 21:19:18, insgesamt 1-mal geändert.

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 21:15:20

OK, ist zu sehen unter http://nopaste.debianforum.de/3457

Zeig mir bitte Deine Lösung.

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 21:20:58

Muß mich berichtigen, ich habe hier doch das gleiche Verhalten. Ich bin auch immer noch der Meinung, daß dieses völlig korrekt ist
Wenn du die Messages trennen möchtest, könntest du das über einen eigenen Delimiter, oder über fixe Länge bewerkstelligen

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 21:25:55

Ich hatte bei meinem ersten Versuch zwischen den zwei "send" Operationen ein "sleep(1)" eingefügt. Dadurch hatte ich "anscheinend" das von dir gewünschte Verhalten.

http://nopaste.debianforum.de/3458

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 21:27:33

Die Länge ist doch fixiert. Es sind immer 8 Bytes die Übertragen und gelesen werden. Die beiden Strings, die schreibe sind "**LAN**" und "**END**"
Beide sind 7 Zeichen plus einem 0-Byte lang.
Und diese werden auch so vom Server angenommen, also sollte zumindset so sein. :?:

Ich wollte danach dann einen Client/Server erstellen, wo zunächst die Länge des Strings übertragen wird und dann der String selbst und das ganze dann auch noch mit select. Aber erst mal muß ich das in den Griff bekommen mit einfachen Strings mit fixer Länge.
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 18.06.2006 21:40:31

wenn du mein Code testest, siehst du daß bei mir der komplette Text ("**LAN****END**" )ankommt. Das ist der einzige Unterschied, aber dieses Verhalten hättest du auch, wenn du den Lesebuffer größer gestalten würdest

Code: Alles auswählen

Die Länge ist doch fixiert. Es sind immer 8 Bytes die Übertragen und gelesen werden. Die beiden Strings, die schreibe sind "**LAN**" und "**END**" 
Beide sind 7 Zeichen plus einem 0-Byte lang.
senden tust du derzeit 7 Bytes (strlen) und empfangen möchtest du 8 Bytes, deshalb ist das bei dir so komisch formatiert.
Du brauchst nur beim Aufruf von "recv" die 8 in eine 7 umändern

Gruß
gms

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 18.06.2006 21:44:24

OK, probiere ich gleich Zuhause. Ich werde gerade hier http://info.imsd.uni-mainz.de/ verscheucht. Zuhause habe ich im Moment kein Internet :oops:

Ich kann Dir dann morgen sagen obs klappt.

Danke und Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

Benutzeravatar
Columbus
Beiträge: 1051
Registriert: 30.04.2002 15:25:02
Wohnort: Mainz
Kontaktdaten:

Beitrag von Columbus » 19.06.2006 15:08:30

Jo, hat geklappt. Ich schicke einen String mit 7 Zeichen und einem 0-Byte mit Send ab und empfange ihn mit recv mit einem Puffer von 7 Zeichen absolut.

Dann wird alles richtig empfangen.

VIelen Dank noch mal.

Gruss Christian
Die größten Kritiker der Elche,
waren früher selber welche.

F.W. Bernstein

Antworten