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.
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.
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
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;
}
}
}
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
unsignedchar*readData()
2
{
3
SOCKADDR_INremoteAddr;
4
intremoteAddrLen=sizeof(remoteAddr);
5
intbytesReceived=0;
6
inttotalBytesReceived=0;
7
intexpectedBytes=0;
8
unsignedchardata[256];// kann nicht größer sein, da die
9
// gesendete Längenangabe 1 Byte ist.
10
unsignedchar*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
elseif(bytesReceived==SOCKET_ERROR){
48
free(returnData);
49
returnNULL;
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
returnreturnData;
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
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.