PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : socketprogrammierung in c


Gast
2006-04-21, 23:19:10
ich hab ein bisschen damit rumgespielt, aber habe ein problem.

so empfange ich eine nachricht:

memset(buf, '\0', sizeof(buf));
recv(client_socket, buf, sizeof(buf), 0);


das memset ist wahrscheinlich überflüssig, nur versuche ich irgendwie mein problem zu lösen. beim recv wird irgendwie kein \0 ans ende der nachricht geschrieben, sodass ich immer die vorige nachricht (wenn sie länger war) dabei hab. das memset hilft auch nicht, also scheint es wohl am socket zu liegen. kann ich den irgendwie flushen oder so?

hier ein beispielablauf meines problems:

empfangen: "beispiel nachricht"
inhalt von "buf": "beispiel nachricht"
empfangen: "test"
inhalt von "buf": "testpiel nachricht"

versteht ihr was ich meine? wie krieg ich diesen socket nur leer?
ich weiss, dass manche leute mit tricks arbeiten, wie zum beispiel vor jeder nachricht die länge der nachricht in exakt einem byte rüberschicken oder so, aber da muss dann die gegenstelle mitspielen, was sie in meinem falle nicht tut (hab ich keinen einfluss drauf)

Gnafoo
2006-04-21, 23:35:46
Afaik gibt recv als Rückgabewert die Anzahl der gelesenen Bytes zurück. D.h. folgendes sollte funktionieren: (zumindest so in der Art :D)


char buf[BUFFER_SIZE];
int bytes_read = recv(client_socket, buf, sizeof(buf)-1, 0);

if(bytes_read > 0)
{
buf[bytes_read] = 0;
// ... ausgabecode ...
}
else
{
// fehlerbehandlung etc.
}


Wobei man natürlich nochmal mit recv weitere Daten empfangen sollte, wenn die Nachricht die Puffergröße überschreitet.

Trap
2006-04-22, 00:23:10
TCP überträgt einen Stream von Bytes, keine Pakete. Was auch immer man programmiert, es muss auch funktionieren wenn man immer nur 1 Byte auf einmal bekommt und es muss auch funktionieren wenn man mit einem recv mehrere Nachrichten auf einmal bekommt.

Sinnvollerweise schickt man immer (Nachrichtenlänge, Nachrichtentyp, Daten) oder etwas in der Art. Dann kann man als Empfänger einfach entscheiden wo die Grenzen der Nachrichten sind.

Wie trennt deine Gegenstelle die verschiedenen Nachrichten?

Gast
2006-04-22, 02:11:52
soweit ich weiss gar nicht. eventuell muss ich da in der theorie noch was nachholen.

so stelle ich mir das vor:

1. client sendet etwas zum server.
2. server liest den socket und kriegt die nachricht vom client (bis zum \0)

so sollte es doch funktionieren, oder? bei früheren sachen hab ich einfach immer die nachricht bis zum nächsten \n gelesen, um diese problematik zu umgehen, d a hab ich aber auch server und client programmiert, von daher kein problem.
mehrzeilige nachrichten wurden niemals versendet.

ich schau mir jetzt mal den rückgabewert von recv an. wenn das die anzahl der neuen gelesenen bytes und nicht aller im socket ist, ist mein problem ja schon gelöst.

Gast
2006-04-22, 02:16:44
ne, das ist es immernoch nicht. ich glaube, dass in dem socket einfach immer die komplette nachricht stehen bleibt .... kann ich den nicht irgendwie leerfegen, bevor ich wieder eine empfange?

Gast
2006-04-22, 08:55:32
ich glaube, dass in dem socket einfach immer die komplette nachricht stehen bleibt .... kann ich den nicht irgendwie leerfegen, bevor ich wieder eine empfange?

Klingt eher danach, dass du deinen Buffer nicht leerst. Den musst du natürlich leeren, bevor du ihn wieder füllst.

Gast
2006-04-22, 08:58:25
mehrzeilige nachrichten wurden niemals versendet.


Entweder kennst du die Länge der Nachricht oder deine Nachricht enthält ein Stopzeichen. Wenn du bisher nie mehrzeilige und wahrscheinlich auch noch kleine Nachrichten versendet hast, bist du eben ganz einfach der Problematik umgangen.

Gast
2006-04-22, 16:39:39
ich leere den buffer, siehe das memset da oben.
der SOCKET wird aber nicht geleert, und ich denke, dass das doch möglich sein MUSS.

bisher bin ich das wirklich immer umgangen, aber es muss doch zu schaffen sein .... man muss doch irgendwie diesen blöden socket flushen können.

Dr.Doom
2006-04-22, 20:00:44
ich leere den buffer, siehe das memset da oben.
der SOCKET wird aber nicht geleert, und ich denke, dass das doch möglich sein MUSS.

bisher bin ich das wirklich immer umgangen, aber es muss doch zu schaffen sein .... man muss doch irgendwie diesen blöden socket flushen können.Hmm, aber wenn du "test" empfängst (Socket) und der Inhalt von "buf" dennoch "testpiel nachricht" ist, dann hat das doch nichts mit dem Socket zu tun?!
Würdest du "testpiel nachricht" schon zugesandt bekommen, dann wärs der Socket.

Keine Ahnung, ob das mit dem memset vorher funktioniert. Ich würds so probiert haben, aber es kommt vmtl aufs Gleiche raus (*unwissend*):
bytes_read = recv(sockelchen, bufferle, sizeof(bufferle), 0);
bufferle[bytes_read] = '\0';

Fruli-Tier
2006-04-22, 20:09:53
Also auf dem Socket liegt meines erachtens nichts mehr von dem, was man zuvor ausgelesen hat. Das müsste ich, wenn ich auf Arbeit mal zufällig wieder Zeit hab, in unseren Libs nachprüfen - die Socketspielereien sind ja von mir :)

Ich gehe auch davon aus, daß dein Buffer einfach nicht richtig geleert wird. Allokiere Dir doch mal vor dem Auslesen einen simplen char* Buffer, speicher Dir den irgendwo weg - das wirst Du ja schon irgendwie machen - dann kill den Speicherbereich (delete). Wenn danach noch irgendwas schief läuft, dann ist, ganz pauschal gesagt, einfach mal Windows Schuld ;P

Gast
2006-04-22, 20:38:29
C, gibt kein delete.

buffer leeren == memset -> ich schreibe ihn komplett mit '\0' voll

so schauts aus:
ich lese genau 1024 byte aus dem socket in meinen buffer.
in diesem socket steht aber nicht die letzte nachricht und dann ein \0, sondern die längste nachricht, darüber geschrieben dann die letzte nachricht.

daher kommt dieses "testspiel" zustande. man muss den irgendwie flushen können ...

Gast
2006-04-23, 00:06:16
zeig mal mehr code!

SimonX
2006-04-23, 02:40:42
Ich würde mal auf nicht initializierten Buffer tippen.

Wahrscheinlich sendest du mit send(...,strlen(buffer)), was aber nicht richtig ist, denn strlen(buffer) enthält kein \0. Da musst du schon strlen(buffer)+1 viele Zeichen versenden, dann bekommt das recv() auch ein 0 Zeichen.

Ausserden kann man anstatt send() und recv() einfach read() und write() benutzen. send() und recv() sind nur für UDP Connections interessant, da man bei ihnen das Ziel bzw. den Sender mitbekommt.

Generell muss man bei Sockets immer von Datenfragmenten ausgehen. Das heist, das man im echten Internet z.B. nicht einfach recv(buf, sizeof(buf) ...) machen kann und dann davon ausgehen kann, das man wirklich eine komplette Message hat.

Falls man weis wie lang die Message ist, dann muss man sowas machen:

static int ReadMessage(int fd, char *msg, int len)
{ int r;
int orig_len=len;
while (len>0) {
r=read(fd, msg, len);
if (r==0) { return 0; /*EOF*/}
if (r<0) { return -1; /* Real error */ }
msg+=r;
len-=r;
}
return orig_len;
}

Wenn man nicht weis wie lang die Message ist, dann muss man nach dem Terminierungszeichen suchen und man muss darauf vorbereitet sein, das mehr als nur eine Message im Buffer ist. Wenn man das effizient machen will benutzt man einen eigenen Buffer, denn man mit dem recv() oder read() füllt und nach jedem füllen den buffer auf das Terminerungszeichen prüft. Findet man es, dann gibt man die Message nach oben und verschiebt den Bufferinhalt, den man noch nicht bearbeitet hat an den Bufferanfang (mit memmove() ).


@Der Tod
Ein Returnwert von 0 von recv zeigt an, das die Verbindung (nur bei TCP nach connect/accept) unterbrochen wurde. Also ist das nicht unbedingt ein Fehler, sondern eher eine normale Situation.

Gast
2006-04-23, 11:49:28
hm, dann bleibe ich wohl einfach dabei, als terminierungszeichen das \n zu verwenden. nur komme ich mir dabei halt irgendwie etwas unelegant vor - ich könnte immer noch schwören dass man einen socket irgendwie flushen kann, aber ich mache es einfach wie bisher.

Fruli-Tier
2006-04-23, 13:39:14
Ein Socket wird geleert, wenn er gelesen wird, da is nix mit flushen.