Quote:
Originally Posted by bloodx
Ich benutze diese Funktion in einem Emulator.
Danke MrSm!th für deine erste Antwort, die war wirklich super !
Ich hatte bisher nur angst vor einem Overflow, darum ging es mir.
Wäre es Sinnvoll für die bekannten Packet's die Size zu prüfen einfach ? Das würde ja schon viele Sachen verhindern.
Ich kann ja also auch einfach
PHP Code:
if ( ( iReceivedBytes <= 0 ) || ( iReceivedBytes >= 1024 ) ) { closesocket( this->ClientSocket ); return 0; }
machen um direkt irgendwelche Phantasie Packets zu entfernen ?
Mit der Size auslesen hatte ich auch bereits überlegt, aber dann muss ich ja 2x recv'n ? Fand ich auch nicht sooo super, trotzdem Danke :)
|
Du scheinst die Funktionsweise von recv etwas misszuverstehen. Letztendlich kannst du das wirklich mit einer ReadFile Funktion vergleichen, nur dass du nicht von der Festplatte, sondern vom Netzwerkpuffer liest. Dabei blockiert die Funktion, wenn es nichts zu lesen gibt und hört dann auf zu lesen, wenn der aktuelle Lesevorgang beendet ist (die maximale von dir erlaubte Anzahl an Bytes wurde erreicht, die Verbindung wurde geschlossen, das System hat entschieden, dass erstmal genug empfangen wurde, ...), belässt die eventuellen restlichen Bytes im Puffer und macht dort beim nächst recv Aufruf direkt weiter. Diese Daten haben für send/recv keinerlei Struktur, es ist lediglich ein Strom von Bytes.
Wenn du auf der Senderseite 1 Packet (im Sinne deiner definierten Packetstruktur) verschickst, bedeutet das nicht zwangsläufig, dass es direkt in einem Stück ankommt. Ebenso bedeuten 2 Packets nicht gleich, dass du zwei recv Aufrufe brauchen wirst.
Du schreibst die Bytes auf der einen Seite in den Puffer rein und nimmst sie auf der anderen raus.
Worauf ich hinaus will: Wenn du mehr als 1024 Bytes im Puffer hast, heißt das nicht gleich, dass dort ein Fantasiepacket gesendet wurde. Es können auch einfach bereits mehrere Packets angekommen sein (sofern dein Protokoll nicht vorsieht, dass ein weiteres Packet erst nach einer Antwort des Servers kommen darf). Der Puffer, den du für recv als Zwischenspeicher angibst, ist
nicht gleichzusetzen mit einem fertigen Packet; seine Größe ist ebenso wenig wie die Anzahl der empfangenen Bytes gleichzusetzen mit der Größe eines Packets. Es ist nur ein Zwischenspeicher. Die Packets musst du dir selbst zusammenbauen bzw. diesen Datenstrom sinnvoll in diese aufteilen.
Dementsprechend kann deine Struktur z.B. so aussehen:
Code:
const int MAX_DATA_SIZE = 100;
struct Packet
{
int magic_signature;
int protocol_version;
int packet_id;
int data_size;
char[MAX_DATA_SIZE];
};
Dein Puffer muss nun nicht zwingend sizeof(Packet) groß sein, du kannst aus Effizienzgründen durchaus mehr auf einmal lesen:
Code:
std::vector<Packet> received_packets;
const int MAX_BUFFER = 1024;
char buffer[MAX_BUFFER];
int buffer_pos = 0;
while ((int recv_bytes = recv(socket, buffer + buffer_pos, MAX_BUFFER - buffer_pos, 0) > 0)
{
int processed_bytes = 0;
//solange vollständige Packets extrahiert werden können
while (recv_bytes - processed_bytes >= sizeof(Packet))
{
Packet temp = *(Packet*)(buffer + processed_bytes);
if (temp.magic_signature == 1337 && temp.protocol_version == 1 && temp.data_size <= MAX_DATA_SIZE)
{
received_packets.push_back(temp);
}
else
{
//issue error
}
processed_bytes += sizeof(Packet);
}
//Rest an den Anfang des Puffers schieben und im nächsten Durchlauf weitermachen
memcpy(buffer, buffer + processed_bytes, recv_bytes - processed_bytes);
buffer_pos = recv_bytes - processed_bytes;
}
(ich weiß, ist kein schönes C++ bzw. ein C/C++ Mix, schlagt mich doch :<)
In diesem Beispiel habe ich der Einfachheit halber mit einer statischen Packetgröße gearbeitet (obwohl der genutzte Platz (data_size) innerhalb des Packets natürlich je nach Packet ID variieren kann).
Mit diesen fertig zerstückelten Packets kann man dann auch anständige Plausibilitätsprüfungen durchführen, so wie ich es dort exemplarisch mit ein paar typischen Bestandteilen eines Packets tue.
Quote:
|
Mit der Size auslesen hatte ich auch bereits überlegt, aber dann muss ich ja 2x recv'n ? Fand ich auch nicht sooo super, trotzdem Danke
|
Damit sollte auch das geklärt sein: Nein, musst du nicht. Du rufst recv immer gleich auf und das hat nichts mit der Anzahl oder der Größe von Packets zu tun. Du baust du dir erst aus dem empfangenen Strom zusammen und erst dort findet die Interpretation von irgendwelchen mitgesendeten Größen und was nicht alles statt.
received_packets kann dann an irgendwelche Callbacks zur Weiterverarbeitungen gegeben werden.
Quote:
|
Wie willst du die Dynamik sonst hineinbringen? Du hast seine Frage ja schon korrekt beantwortet, Shawak und ich haben nur das ergänzt was für manche vielleicht nicht so offensichtlich ist.
|
Gar nicht, der Puffer wird immer statisch bleiben. :p
Quote:
|
Wieso willst du das Socket schließen wenn keine Bytes gelesen wurden? Ich kenne die Implementierung deiner Library nicht aber wenn du vom Socket liest und feststellst, dass dir inzwischen niemand etwas gesendet hat, wirst du vermutlich in diesem Moment 0 Bytes empfangen haben (weil 0 Bytes gelesen wurden). Möchtest du dann wirklich die Verbindung trennen?
|
Wenn recv 0 zurückgibt, wurde die Verbindung von der anderen Seite geschlossen. Man kann nicht 0 Bytes empfangen, da recv eine blockierende Funktion ist, sprich sie wartet ggf. auf Input.
recv ist übrigens die Standardfunktion zum Empfangen auf Windows und Unix-Systemen.