PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Sockets] Immer nur erstes Paket sicher gesendet


Die_Backe
12.03.2006, 20:07
Hallo,
Um ein grafisches Programm von mir besser debuggen zu können, hab ich ein Debugger-Tool geschrieben, dem das grafische Programm über einen Socket Text schicken kann, den der Debugger dann auf der Konsole ausgibt. Dazu verwende ich eine selbst geschriebene Socket-Klasse und eine ebenfalls selbst geschriebene Debugger-Klasse. Der Debugger horcht dauerhaft auf dem Debug-Port, und connected beim Programmstart auf dem Servercheck-port (beide ports in einem header definiert). Das Programm seinerseits baut beim Starten eine Verbindung auf dem Debug-port auf, und horcht auf dem servercheck-port, falls der Debugger nachträglich startet. Die Klasse "Debugger" spielt dabei die Rolle, dass sie auf der Programmseite die Verbindung aufbaut und hält.
Das Problem ist jetzt folgendes: Wenn ich mehrere Nachrichten gleichzeitig versende, also im Stil
gDebugger<<"PLONK1\n"<<"PLONK2\n"<<"PLONK3\n"<<"PLONK4\n;
, dann kommt auf jeden Fall das erste Plonk an, aber von den anderen Plonks fehlen die meisten. Die Reihenfolge der Pakete stimmt, aber, dass wirklich alle ankommen passiert so gut wie nie. Wenn ich breakpoints setze, um zu gucken wo das Paket stecken bleibt, geht plötzlich alles... der Fehler tritt nur auf wenn die Pakete nichtnur im Code, sondern auch praktisch gleichzeitig versandt werden.
Ich verwende GCC auf meinem Windows XP (Dev-Cpp als IDE).


Soviel zum Problem, jetzt kommt nurnoch verwendeter Code (gekürzt natürlich, für das Wohl der Forennutzer. Tut mir leid, weiter kann ich das Problem nicht eingrenzen):
Aus der Socket-Klasse:
(mHandle ist der sockethandle des jeweiligen sockets)

Socket::Socket()
{
Socket::init();
if ((mHandle = ::socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
return; //unhandled error
};

mIsConnected = false; //wird bei connect() auf true gesetzt.
};
Socket::~Socket()
{
closesocket(mHandle);
};
Socket& Socket::operator<<(const char* msg)
{
this->send(msg);
return *this;
};
int Socket::send(const char *msg, int flags = 0)
{
this->send(msg, strlen(msg) + 1, flags);
};
int Socket::send(const char *msg, size_t len, int flags = 0)
{
if (::send(mHandle, msg, len, flags) == -1)
{
return -1;
};
};
int Socket::recv(char *buffer, size_t len, struct timeval* timeout = NULL, int flags = 0)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(mHandle, &fds);

::select(mHandle + 1, &fds, NULL, NULL, timeout);

if (FD_ISSET(mHandle, &fds))
{
int bytes;
if ((bytes = ::recv(mHandle, buffer, len, flags)) == -1)
{
return -1;
};
return bytes;
};

return 0;
};


und der Debugger (also die Klasse, die auf der Programm-Seite die Daten Sendet)

void Debugger::send(const char* msg)
{
//If the Debugger Program was not found yet
if (!this->IsConnected()) //guckt ob der Debug.Socket connected ist
{
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;

//look whether it has started
Socket* s = mServer.accept(&timeout);
if (NULL != s)
{
//if Debugger-Program has started, connect to it
delete s;
/* mClient ist das Socket-Objekt, das auf dem Debug-Port sendet. */
mClient.connect("127.0.0.1", PORT_DEBUG);
}
else
{
return; //unhandled error, accept() failed.
};
};
//send the messange
mClient<<(msg);
};


Und das Debuggerprogramm sieht im Kern so aus:

for(;;)
{
Socket *sock = serversocket.accept();
if (sock == NULL)
{
perror("accept() failed");
system("pause");
continue;
};
cout<<"Client is connecting..."<<endl;
char buffer[BUFFER_SIZE];
int bytes;

while((bytes = sock->recv(buffer, sizeof(buffer))) > 0)
{
cout<<buffer;
buffer[0] = 0;
};
delete sock;
sock = NULL;
cout<<"Connection Closed."<<endl;
};


Edit: Hab nicht mitgedacht und ins falsche Forum gepostet... Das Wort in den Eckigen Klammern sollte jetzt vllt lieber C++ und nicht Sockets heißen...


butterkeks
12.03.2006, 20:40
Mhhh so auf die Schnelle sieht alles richtig aus.

Hast du probiert, in einem packet sniffer (tcpdump, ...) nachzusehen, ob die Pakete rausgehen und zumindest das Betriebssystem die Pakete wieder rein bekommt?

Zur Sicherheit würde ich die Null-Bytes nicht mitsenden und stattdessen den Rückgabewert von recv() nutzen, um die Länge des Strings zu ermitteln. Natürlich sollte es bei TCP nicht nötig sein, aber kann ja nicht schaden.

Die_Backe
12.03.2006, 22:03
Ok, ich hab das mal über das Internet mit einem Freund ausprobiert, und weiß jetzt _was_ passiert, aber nicht genau warum, oder wie ich es beheben kann...
folgender Code:
gDebugger<<"PLONK1\n"<<"PLONK2\n"<<"PLONK3\n"<<"PLONK4\n"<<"PLONK5\n";
Als erstes kommt da das erste Plonk in einem Paket:
50 4c 4f 4e 4b 31 0a 00 PLONK1\n\0
das kommt an, wird aufgefangen und ausgegeben. Dann kommt ein weiteres Paket:
50 4c 4f 4e 4b 32 0a 00 50 4c 4f 4e 4b 33 0a 00 50 4c 4f 4e 4b 34 0a 00 50 4c 4f 4e 4b 35 0a 00
PLONK2\n\0PLONK3\n\0PLONK4\n\0PLONK5\n\0
das wird nach dem nächsten Plonk mit \0 terminiert, und das wars...
besagter Freund hat ne recht langsame Internetverbindung, bei ihm kamen immer abwechselnd plonk1 und plonk2 an, deckt sich ja perfekt mit dem was der sniffer anzeigt.
Als Ansatz um das zu fixen würde ich vielleicht den Rückgabewert des recv() syscalls nehmen, der afair die Zahl der erhaltenen Bytes enthält. Damit kann man sehen ob nach dem \0 noch sinnvolle Daten kommen. Das müsste also an der recv-Methode gefixt werden. Die könnte zB durch den gelesenen String iterieren, und alle Zeichen != \0 in ein anderes Array kopieren, bis die von recv() gelesene Anzahl Bytes erreicht ist.
Klingt das plausibel genug? :D

edit: schonmal danke wegen der Idee mit dem Sniffer ;) bin ich vorher nicht drauf gekommen

edit2: ok, in der recv()-Methode steht jetzt folgendes:

int Socket::recv(char *buffer, size_t len, struct timeval* timeout, int flags)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(mHandle, &fds);

::select(mHandle + 1, &fds, NULL, NULL, timeout);

if (FD_ISSET(mHandle, &fds))
{
int bytes;
if ((bytes = ::recv(mHandle, buffer, len, flags)) == -1)
{
return -1;
};

char result[bytes];
strcpy(result, buffer);
int j = 0;
for (int i = 0; i<=bytes; i++)
{
if (result[i] != 0)
{
buffer[j] = result[i];
j++;
};
};
buffer[j] = 0;


return bytes;
};
};


Ab und zu, vor allem wenn wirklich viele Pakete in kurzer Zeit kommen, gibt der Debugger ejtzt Datenmüll aus, aber meistens klappt das :) großes thx. Warum der jetzt noch manchmal mist ausgibt gilt es noch zu klären.

butterkeks
12.03.2006, 23:20
mhhh vlt. mischt das OS die Pakete ja, wenn sie in WIRKLICH kurzer Zeit hintereinander gesendet werden (dann spart man sich immerhin einige TCP/IP Frames, bzw. noch mehr, wenn man nicht nur an localhost sendet).

Aber andererseits frage ich mich, warum alles \0 bytes außer dem letzten rausfallen, obwohl du doch alle reinschreibst...

Meine Lösungsvorschläge:
- Selber Puffern (bei einem Debugger ist allerdings jegliche Pufferung wohl eher schlecht. Du könntest den Puffer alternativ auch nach einer halben Sekunde oder so leeren und nicht erst, wenn er voll ist)

- Dein Socket blockt zwar, aber vlt. solltest du es dennoch mal mit einem select() probieren, das nachfragt, ob Daten gesendet werden dürfen, ohne irgendwelche Probleme zu verursachen

Kannst du beschreibem, wie der Datenmüll ungefähr aussieht? Vlt. fällt mir noch was ein...

Die_Backe
13.03.2006, 17:37
Folgender Versuch:
gesendet mit
gDebugger<<"1"<<"2"<<"3"<<"4"<<"5"<<"\n";
Ausgegeben, buchstabe für buchstabe mit der Zeile
cout<<hex<<(unsigned short) buffer[i]<<" "<<buffer[i]<<"\n";

hmm, also:

manchmal werden Zeichen durch ein "\4" ersetzt, in meiner ASCII-Tabelle als EOT benannt, und sichtbar als ein karo aus einem französischen kartenspiel.
manchmal ist der zweite gesendete Buchstabe ein "\12" oder C in Hexadezimal. Das heißt laut Tabelle NP. Dirket auf den folgt nochmal die vollständige Abfolge von Anfang an. Also zB erst "1\12" und dann "12345\n".
ein für mich nciht ersichtliches Problem verursacht das Zeichen "\8", bezeichnet mit BS. Da wo ein \8 steht scheinen größere Lücken zu sein, so in etwa, wenn 2 Pakete kommen: "123[\8]12[\8]5\n"

Mitgesnifft hab ich diesmal nicht, weil Pakete von localhost an localhost nicht aufgefangen werden, und ich auf die Schnelle niemanden gefunden hab der mir dabei hilft.. hol ich aber nach.

Die_Backe
16.03.2006, 19:14
ok, dies hier funktionniert nun endgültig:


//clearing out the \0-chars
int writePos = strlen(buffer);
int readPos = 1 + strlen(buffer);
for (; readPos < bytes; readPos++)
{
while (buffer[readPos] == '\x0')
{
readPos++;
};
if (readPos < bytes)
{
buffer[writePos] = buffer[readPos];
writePos++;
};
};
buffer[writePos] = '\x0';


mfg Backe