Hallo ich hab ein kleines Problem mit recv. ich übergebe der Funktion z.B. einen buffer von 100 Zeichen und empfangen werden aber nur 10 oder irgendeine andere Anzahl von Zeichen, was mache ich falsch Gruß, Markus
hi, Deine Angaben sind schon etwas spärlich! Über winsock (generell TCPIP) werden die Daten immer paketweise verschickt. Kleine Datenmengen gehen auf einen Rutsch durch, größere werden stückeweise verschickt. du solltest dir ein "endzeichen" einfallen lassen und solange die Daten sammeln, bis dein endzeichen da ist. Wenn du noch schreibst, in welcher Progammiersprache, dann kann man noch besser helfen. mfg Thomas
ich schreibe in C, hab mir da eine Funktion definiert. char * readData(int buf) { long byte = recv(sock,data,...) return data[byte]='\0'; } Nur irgendwie ist "data" jetzt immer ein leerer String, obwohl Daten vorhanden sind. Ich habe zur Überprüfung das Terminal von Herkules benutzt, da funktionierts einwandfrei.
ich will es für alle beiden Protokolle machen. Der Unterschied besteht ja soweit ich weiss nur in der Verwendung einer anderen Funktion, statt recv heißt es dann eben recvfrom.
Markus schrieb: > Der Unterschied besteht > ja soweit ich weiss nur in der Verwendung einer anderen Funktion, statt > recv heißt es dann eben recvfrom. Ui. Diese Aussage hinkt gewaltig. Das ist für mich ungefähr so, wie wenn einer sagt, der Unterschied zwischen Free-Climbing und 'Mit-der-Seilbahn-auf-den-Berg-fahren' besteht in einer anderen Rucksackform.
bei TCP musst du solange in einer Schleife lesen bis - alle Zeichen angekommen sind: dazu den pos. return Wert von recv auf den Lesezeiger oder Bufferindex aufaddieren und die Anzahl von der Sollanzahl abziehen bis die Diff. 0 ist - oder recv eine 0 oder einen Fehler zurückliefert, dann Verbindung schliessen und wieder connect oder listen starten
mir ist der Unterschied zwischen TCP und UDP schon klar. Mein Problem ist aber die recvfrom Funktion, diese gibt mir zwar die Anzahl der Bytes zurück, z.B. 25 aber wenn ich die Daten dann ausgeben will ist der String seltsamer weise manchmal leer. Ich steuere ja ein Messgerät an, ich schicke ein Befehl hin (funktioniert) und warte auf die Bestätigung und danach auf die Daten. Manschmal geht es auch teilweise, dann kann ich z.B. die Befehlsbestätigung auslesen, nur eben nicht immer. Das will mir nicht in den Kopf.
> mir ist der Unterschied zwischen TCP und UDP schon klar. Mein Problem > ist aber die recvfrom Funktion, diese gibt mir zwar die Anzahl der Bytes > zurück, z.B. 25 aber wenn ich die Daten dann ausgeben will ist der > String seltsamer weise manchmal leer. Ich steuere ja ein Messgerät an, > ich schicke ein Befehl hin (funktioniert) und warte auf die Bestätigung > und danach auf die Daten. Manschmal geht es auch teilweise, dann kann > ich z.B. die Befehlsbestätigung auslesen, nur eben nicht immer. Das will > mir nicht in den Kopf. Dein Fehler ist in der Anschauung, dass du mit einem recv die Daten genau so bekommst, wie der Sender sie weggeschickt hat. Dem ist nicht so. Der ganze Netzwerkunterbau kann die Daten in mehrere Pakete aufteilen und getrennt auf den Weg schicken. Es kann auch sein, dass der Netzwerkunterbau schon den Anfang der Daten in einem Paket weggeschickt hat, während der Datenerzeuger eine kleine Pause eingelegt hat um die nächsten Daten für das (seiner Meinung nach) immer noch gleiche Paket zusammenzustellen. Du brauchst eine Minimalform eines Protokolls. Ohne geht es nicht. Entweder der Sender schickt ganz am Anfang mit, wieviele Bytes er zu senden gedenkt oder in den Daten ist eine Markierung drinnen "Jetzt ist der Datensatz komplett". Deine Leseroutine muss unter Umständen recv mehrmals aufrufen um den Datensatz wieder komplett zusammenzustellen. Und genau dafür benötigst du vom Sender noch zusätzliche Information, wann das der Fall ist. Eben dann, wenn die Anzahl der Bytes übertragen wurde bzw. wenn die Daten mittels einer Markierung als zu Ende markiert wurden. UDP ist dann noch ein ganz anderes Thema. Da können deine Daten, so sie in mehreren Paketen aufgeteilt sind, in beliebiger Reihenfolge daherkommen; da kann dann auch schon mal ein Datenpaket fehlen etc. Sprich: Du kannst dich auf gar nichts mehr verlassen.
ja du hast recht, mein Messgerät schickt zuerst ein Byte, welches die Anzahl der nächsten dazugehörigen Bytes beschreibt. An Mehrfach-Aufruf hab ich schon gedacht, aber das ist ja erst der nächste Schritt. ich sehe ja auch Daten, wenn ich am Puffer rumspiele, also ihn z.B. 50 setze. Ich hatte mir gedacht, wenn ich ihn auf 1 setze kann ich dann immer ein Byte auslesen, da bekomm ich aber einen Socket_ERROR. Und eine andere Frage, ich habe auch noch das Problem das der Befehlssatz meines Messgerätes auf hexadezimalzahlen beruht. Dies hab ich gelöst das ich in die ASCII-Tabelle geschaut habe und das entsprechende Aquivalent zur entsprechenden Hexadezimalzahl gesucht und als String bei sendto übergeben habe, ist aber sehr mühselig, gibt es eine Funktion in C mit der man Hexadezimalzahlen in einen String mit ASCII-Zeichensatz umwandeln kann oder bzw. gibt es eine Möglichkeit einen String so zu markieren das recv versteht, das es sich um hexadezimalzahlen handelt und nicht um den ASCII Zeichensatz.
Markus schrieb: > ja du hast recht, mein Messgerät schickt zuerst ein Byte, welches die > Anzahl der nächsten dazugehörigen Bytes beschreibt. An Mehrfach-Aufruf > hab ich schon gedacht, aber das ist ja erst der nächste Schritt. Nein, das ist nicht der nächste Schritt. Das ist der erste Schritt: Aus den einlangenden Paketen wieder einen Datenstrom zu machen, aus dem die Datensätze herausgefischt werden. > einen String so zu markieren das recv versteht, das es sich um > hexadezimalzahlen handelt und nicht um den ASCII Zeichensatz. Auf dieser Ebene gibt es keine Strings mehr. Da gibt es nur noch Bytes. Was immer du an Bytewerten in dein Bytefeld hineinstopfst, bleibt auch so drinnen. Du Tatsache, dass du hier von Strings redest, lässt mich schon Schlimmes vermuten. PS: Lass dich nicht davon verwirren, dass sendto auf deinem System ev. einen char* als Argument nimmt. char* war früher die übliche Schreibweise für 'Pointer auf alles'. Heutzutage würde man einen void* dafür benutzen, aber es ist nichts schwerer auszurotten als alte Gewohnheiten bzw. Kompatibilitäten.
1 | unsigned char SendBuffer[10]; |
2 | |
3 | SendBuffer[0] = 0x12; |
4 | SendBuffer[1] = 0x78; |
5 | SendBuffer[2] = 'd'; |
6 | SendBuffer[3] = 'e'; |
7 | |
8 | sendto( serverSocket, SendBuffer, 4, ... |
9 | |
10 | //
|
11 | // oder:
|
12 | // sendto( serverSocket, (char*)SendBuffer, 4, ...
|
danke für die hilfreichen Antworten, ich werd ma schaun ob ich es hinbekomme, das mit dem char hat mich wirlich etwas Wirre gemacht.
eine Frage muss ich doch noch stellen, von meinem Messgerät(laut manual) müsste ich eigentlich hexadezimale Zahlen empfangen, das Problem ist nur das diese Hexadezimalzahlen von recv wieder als ASCII-Zeichenfolge ausgegeben werden .z.B FO wird als ö ausgegeben, wie bekomme ich das nun hin, das als hexadezimal zeichen auszugeben. zu meiner Verdeidigung möchte ich noch sagen, das ich kein Informatiker der Programmierer bin. Ist alles Neuland für mich
gast schrieb: > eine Frage muss ich doch noch stellen, von meinem Messgerät(laut manual) > müsste ich eigentlich hexadezimale Zahlen empfangen, das Problem ist nur > das diese Hexadezimalzahlen von recv wieder als ASCII-Zeichenfolge > ausgegeben werden .z.B FO wird als ö ausgegeben, wie bekomme ich das nun > hin, das als hexadezimal zeichen auszugeben. zu meiner Verdeidigung > möchte ich noch sagen, das ich kein Informatiker der Programmierer bin. > Ist alles Neuland für mich Von deinem Messgerät kommen weder Texte noch hexadezimale Zahlen noch sonst irgend etwas. Von deinem Messgerät kommen Bytes, die einen Wert haben. Wie du diese Bytes anzeigst ist dein Bier. Ob F0 als ö ausgegeben wird oder als 0xF0 oder als 240 oder als Pixelmuster, hängt ausschliesslich davon ab, wie du ausgibst. Deswegen ändert sich aber der Bytewert nicht. Der Bytewert ist immer dergleiche: die oberen 4 Bit auf 1, die unteren 4 Bit auf 0 Im Rechner gibt es keine Texte, es gibt keine Hexwerte, es gibt keine Dezimalzahlen. Es gibt ausschliesslich Bitmuster! Was diese Bitmuster bedeuten, interessiert den Rechner nicht. Ein Bitmuster kann alles mögliche sein: Sie kann ein Opcode sein, der einen Befehl für die CPU kodiert. Es kann mithilfe des ASCII Codes als ein bestimmmter Buchstabe (bei der Ausgabe) interpretiert werden. Nimmt man anstelle des ASCII Codes den EBCDIC Code, so wird dasselbe Bitmuster als ein anderer Buchstabe angezeigt. Es kann bei der Ausgabe wahlweise als Hex-, Dezimal-, Binär-, Oktal- oder zu jeder anderen beliebigen Basis ausgegeben werden. Das Bitmuster kann als Codierung augefasst werden, welche Lämpchen aufleuchten sollen und welche nicht oder welche Relais ein bzw. aus sein sollen .... Aber egal, wie dieses Bitmuster bei der Ausgabe interpretiert wird, im Rechner ist es immer nur dasselbe: eine Zahl gebildet aus dem 1-0 Bitmuster von ein paar Bits (genaugenommen ist es im Rechner eigentlich nur ein Bitmuster. Selbst die Auffassung einer Zahl ist schon eine Interpretation dieses Bitmusters). Welche Bedeutung diese Bits haben, bestimmst du als Programmierer bzw. spätestens dann, wenn du das Bitmuster irgendwo zur Anzeige bringst. > wie bekomme ich das nun > hin, das als hexadezimal zeichen auszugeben Wie wäre es erst einmal damit, ein C-Buch durchzuarbeiten? Ungefähr in Kapitel 3 (von 60) werden dort die ersten Ausgabevarianten durchgesprochen, was man so alles mit dem Formatstring von printf anstellen kann und welches % Kürzel wofür steht. Wenn du dich an Netzwerke heranwagst, solltest du diese Dinge eigentlich aus dem FF beherrschen.
> Wie wäre es erst einmal damit, ein C-Buch durchzuarbeiten?
Wozu? Wenn er oft genug fragt, hilfst du ihm schon.
hab es jetzt gelöst, das Problem besteht nicht mehr. Ich verwende auch ein C-Buch, hab nur eben aufgrund des Zeitdrucks ein paar Kapitel übersprungen. Ich weiss natürlich das auf der Leitung nur Spannung an/aus (1,0) anliegt. Trotzdem wurde bei Programmiersprachen ja sicherlich schon abstrahiert. Im Maschinenbau hat man leider nicht so viel mit Programmierung am Hut. Ich bedanke mich trotzdem für eure Mühe. Ein letztes noch, was ich leider nicht in meinen C-Buch gfunden habe und mir etwas unklar ist: meiner Funktion readData übergebe ich ja den Puffer von recvfrom den Speicherbereich für data hab ich über die malloc funktion realiesiert und als Größe buf gegeben recvfrom(s,data,buf,...) als Rückgabewert geb ich dann die Adresse von data an, zeigt ja auf den Speicherbereich des ersten Zeichens. Das problem war nur das ich da nur ein Zeichen zurückgegeben bekommen habe. Schreibe ich aber &data[1] werden alle eingelesenen Zeichen, abgesehen natürlich vom ersten zurückgegeben. Übergebe ich aber malloc buf + 1 funktioniert das ganze mit &data und alle eingelesenen Zeichen werden zurückgegeben. Das finde ich irgendwie seltsam. Naja hauptsache es geht, falls einer mir das besagte Problem doch noch erklären könnte, würde ich mich freuen - nur zum Verständnis, man will ja auch wissen warum das so geht wie es geht. mfg, Markus
1 | malloc buf + 1 |
wird dir niemand kompilieren, also zurück zum Start! Ich verstehe deine letzte Frage auch nach dreimaligem Lesen nicht so recht. Kann an mir liegen, erfahrungsgemäß ist sie aber wohl einfach zu unklar formuliert. Wo bekommst du nur ein Zeichen? Kann es sein, daß du irgendwo die Daten verwendest, ohne eine abschließende 0 anzuhängen, aber dich auf die 0 verlässt? Z.B. zur Ausgabe eines Strings, den du mit recv() oder so etwas bekommen hast, wirst du in der Regel selbst die terminierende 0 anhängen müssen, bevor du den String mit puts(), printf() o.s.ä. verwenden kannst. Dazu muss der Puffer natürlich auch groß genug sein, also Länge der Nutzdaten + 1.
meine Problem ist das bei ersten Aufruf meiner Funktion alles funktioniert, ich bekomme die zwei Befelsbestätigungszeichen aus meiner Funktion zurück. Beim nächsten Aufruf bekomme ich nur das erste Zeichen zurück, obwohl 25 Zeichen anliegen. Das hab ich überprüft, indem ich mir einfach mal die byt Anzahl zurückgegeben habe. Ich kann es aber auch kompilieren. Gestern hat es auch noch funktioniert, heute nicht mehr. Ich versteh das einfach nicht. char* readData(int buf) { SOCKADDR_IN remoteAddr; int remoteAddrLen=sizeof(SOCKADDR_IN); char *data=malloc(buf); int byt=recvfrom(s,data,buf,0,(SOCKADDR*)&remoteAddr,&remoteAddrLen); if(byt==SOCKET_ERROR ) { int a=closesocket(s) ; WSACleanup() ; static char er[]="NULL"; return er; } else { if (byt == 0) { static char xp[]="keine Daten"; return xp; } else { // data[byt]='\0'; //int len=strlen(data); //char *xa=malloc(byt+1); //strcpy(xa,data); data[byt]='\0'; return data;//&data; } } }
jetzt hab ich zurückgegeben einfach mal &data[2] der erhalte ich auf einmal wieder alle daten bis auf die ersten 2
Wenn du maximal buf Zeichen liest, musst du buf+1 Zeichen allokieren, um Platz für die abschließende 0 zu haben.
In buf übergebe ich 100 Zeichen in einer Endlosschleife. 25<100 -> müsste ja alles okay sein . Hab trotzdem einmal probiert buf +1 an die malloc-Funktion zu übergeben. Dies hat leider keine Besserung gebracht. Nur wenn ich die 3 Adresse übergebe sehe ich alle restlichen Daten. Aber ich brauche natürlich auch die Daten von der Adresse 1 und 2
bei data[1] erhalte ich einen Leerstring, vielleicht ist das ja das Problem. Auf dieser Adresse befindet sich keine Zeichen (auch kein Leerzeichen oder was ähnliches)
> ja ich weiss, aber da befindet sich eben kein Zeichen
doch ist es, jedes element in den daten ist ein zeichen, auch wenn kein
darstellbares. Lass dir das ganze doch mal als Hex ausgeben.
kleiner Tip: Bevor ich mich mit Netzwerk herum ärgere, würde ich vielleicht erstmal die Grundlagen von C lernen. Und bei vernünftigen Fragen (ordentlich beschrieben, lesbarer Quelltext) bekommt man auch leichter vernünftige Fragen. So macht es wenig Spaß.
Markus schrieb: > else > > { > > // data[byt]='\0'; > //int len=strlen(data); > //char *xa=malloc(byt+1); > > //strcpy(xa,data); > data[byt]='\0'; > return data;//&data; > } > } > > } Du hast es immer noch nicht. Lies es von meinen Lippen ab: Auf dieser Ebene hast du keine Strings mehr! Hier gibt es nur noch Bytes, keine Strings. Damit aus einer Abfolge von Bytes in C ein String wird, müssen ein paar wichtige Regeln eingehalten werden! Eine davon ist: Sobald ein 0-Byte auftaucht, ist der String zu Ende! Daher ist die Verwendung von Stringfunktionen auf dieser Ebene absolut kontraproduktiv. Vor allem dann, wenn dir dein Messgerät keine Texte sondern einfach nur eine Bytefolge schickt. Niemand garantiert dir, dass in so einer Bytefolge nicht ein 0-Byte enthalten ist. Es ist sogar sehr wahrscheinlich, dass dem so ist. Um mit Bytes zu arbeiten gibt es die Familie der mem... Funktionen. Vergiss str... an dieser Stelle. Das führt dich immer wieder in eine Sackgasse. Und nochwas: Wenn du mit Bytes arbeitest, dann ist der Datentyp der Wahl 'unsigned char' und keinesfalls 'char'. Das öffnet nämlich den nächsten Sack an Problemen.
> Ich verwende auch > ein C-Buch, hab nur eben aufgrund des Zeitdrucks ein paar Kapitel > übersprungen. Das ist keine gute Ausrede. Du hast ja auch noch die Nachtstunden
> ja du hast recht, mein Messgerät schickt zuerst ein Byte, welches die > Anzahl der nächsten dazugehörigen Bytes beschreibt. Schickt das Messgerät auch noch ein eindeutiges Byte, wenn ein Datensatz zu Ende ist? Das wäre wichtig, damit man die Kommunikation wieder synchronisieren kann, falls aus irgendeinem Grund Sender und Empfänger asynchron werden. Deine Empfangsfunktion müsste in etwa so aussehen
1 | unsigned char * readData() |
2 | {
|
3 | SOCKADDR_IN remoteAddr; |
4 | int remoteAddrLen = sizeof( remoteAddr ); |
5 | int bytesReceived = 0; |
6 | int totalBytesReceived = 0; |
7 | int expectedBytes = 0; |
8 | unsigned char data[256]; // kann nicht größer sein, da die |
9 | // gesendete Längenangabe 1 Byte ist.
|
10 | unsigned char * returnData; |
11 | |
12 | bytesReceived = recvfrom( s, data, sizeof(data), 0, |
13 | (SOCKADDR*)&remoteAddr, &remoteAddrLen ); |
14 | |
15 | // das erste empfangene Byte gibt die Länge des Datensatzes an
|
16 | expectedBytes = data[0]; |
17 | bytesReceived--; |
18 | |
19 | returnData = malloc( expectedBytes + 1 ); // nur um sicher zu gehen |
20 | // 1 mehr und dann zum
|
21 | // Schluss noch ein \0 rein
|
22 | |
23 | //
|
24 | // aus dem empfangenen ersten Paket erst mal alles
|
25 | // auf die Seite bringen was da ist
|
26 | //
|
27 | memcpy( returnData, &data[1], bytesReceived ); |
28 | totalBytesReceived = bytesReceived; |
29 | |
30 | //
|
31 | // warte jetzt auf den Rest der noch fehlt
|
32 | //
|
33 | while( totalBytesReceived < expectedBytes ) { |
34 | bytesReceived = recvfrom( s, data, sizeof(data), 0, |
35 | (SOCKADDR*)&remoteAddr, &remoteAddrLen ); |
36 | |
37 | // Noch Daten eingetrudelt?
|
38 | if( bytesReceived > 0 ) { |
39 | // die neuen Daten an das bisher Empfangene anhängen
|
40 | memcpy( &returnData[ totalBytesReceived ], |
41 | data, |
42 | bytesReceived ); |
43 | // und damit sind wir dem Endziel, nämlich expectedBytes Bytes
|
44 | // zu empfangen, wieder ein Stückchen näher
|
45 | totalBytesReceived += bytesReceived; |
46 | }
|
47 | else if( bytesReceived == SOCKET_ERROR ) { |
48 | free( returnData ); |
49 | return NULL; |
50 | }
|
51 | }
|
52 | |
53 | // Das wars. Die Anzahl der erwarteten Bytes ist da
|
54 | // Zur Sicherheit, wenn es ein String ist, noch hinten
|
55 | // eine 0 anhängen.
|
56 | // Wenn die Daten Binärdaten sind, darf das nicht passieren!
|
57 | returnData[ expectedBytes ] = '\0'; |
58 | return returnData; |
59 | }
|
Achtung: Ich habe den Code nicht getestet sondern blind geschrieben. Er sollte zumindest dicht drann sein. Am besten im Debugger durchsteppen und sich alle Variablen ansehen und was da so alles passiert. Um Fehlerbehandlung musst du dich auch noch kümmern. Was aber auf keinen Fall sein kann: Dass mir eine readData Funktion den socket schliesst. Das ist nicht ihre Aufgabe! Die Funktion soll Daten empfangen. Wenn es dabei ein Problem gibt, soll sie das melden. Aber ansonsten soll sie die Finger vom Socket lassen. Mit dem malloc da drinnen bin ich absolut unglücklich. Ist nur wieder ein Stolperstein, dass der Aufrufer den free vergisst. Besser wäre es, wenn deine Funktion so aussehen würde
1 | int readData( unsigned char * recBuffer, size_t bufferLen ) |
2 | {
|
3 | ...
|
4 | }
|
Die Funktion readData kriegt einen Buffer (der gross genug sein muss, Längenangabe in bufferLen) in dem sie die Daten abzuliefern hat. Als Returnwert liefert sie die Anzahl der Bytes, welche sie in den Buffer gestellt hat.
Markus schrieb:
> ja danke, das ist ein sehr guter Anhaltspunkt.
Diese Funktion ist noch nicht einmal ansatzweise dort, wo sie eigentlich
sein müsste.
Wenn dein Messgerät mehrere Antworten in kurzer Folge hintereinander
schickt, dann wird das Probleme geben. Da müsste man noch eine FIFO
dazwischen schalten, die den überschüssigen Anteil nach den jeweiligen
recvfrom aufnimmt. Und die readData müsste als allererstes in diese FIFO
schauen um zu sehen, ob da noch nicht verarbeitete Bytes rumlungern und
sich zuerst von diesen bedienen, ehe sie mittels recvfrom die FIFO
wieder füllt.
Und aus genau dem gleichen Grund können die memcpy kräftig in die Hose
gehen.
Stell dir einfach vor, aus dem ersten recvfrom kommt diese Bytefolge raus 0x05 0x10 0x20 0x30 0x40 0x50 0x08 0x10 0x20 und dann gehst du am Papier die Funktion durch. Hinweis: In dieser Bytefolge sind 2 Antworten vom Messgerät enthalten! Die erste (mit einer Länge von 5) ist komplett, die zweite (mit einer Länge von 8) ist noch nicht komplett. Trotzdem müssen beide Antworten bei 2 Aufrufen von readData korrekt entschlüsselt und richtig aufgeteilt werden. Bei solchen Sachen ist überhaupt Papierarbeit sehr wichtig! Vor allem wenn man das noch nie gemacht hat! Es bringt nichts, da auf gut Glück den Code zu verändern. Du brauchst reproduzierbare Verhältnisse und du musst dir über jede einzelne Anweisung im klaren sein was sie macht, warum sie da ist und was ihre Aufgabe ist.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.