CAsyncSocket - Datenempfang
-
Ich habe ein seltsames Problem beim Verschicken von Daten...
Zum Rumexperimentieren an meiner Socket-Klasse habe ich mir ein Testprogramm geschrieben indem ich Texteingaben und Bilder von Client zu Server und umgekehrt verschicke.
Das Problem ergibt sich beim empfangen (vermute ich stark !!):
Der Socket empfängt Bitmapinformationen und -Bits. Beim darstellen des Bitmaps wird dieses dann 'verzerrt' dargestellt. Es ist nur noch ungefähr zu erkennen, wie das Bitmap aussehen sollte.
Beim Genauen hinsehen habe ich festgestellt, dass 'OnReceive()' das Bitmap zwar empfängt, aber mir einen riesengroßen Wert von 'CAsyncSocket::Receive()' zurück gibt !?!
Ich empfange ca. 60kb an Daten. Es wird mir aber ein Wert im Gigabyte-Bereich ausgegeben.
Das Seltsame ist, dass es manchmal funktioniert. Dann liefert mir 'CAsyncSocket::Receive()' auch einen korrekten Wert und das Bild wird vernünftig dargestellt.
Ich kann nicht erkennen, wann der Fehler auftritt. Das Problem besteht sowohl beim Senden von Server zu Client als auch umgekehrt.
Das Programm stürzt manchmal ab (Debuginformationen sind nicht verfügbar), ich vermute, dass es mit diesem Problem zusammenhängt.
-
sorry. es hat sich herausgestellt dass es ein 'WSAEWOULDBLOCK'-Problem ist
-
ich komme einfach nicht weiter ...
Das Problem tritt auf wenn ich größere Datenmengen hin und her schicke denke ich (Bei Strings von Client zu Server und umgekehrt habve ich keine Probleme).
Ich habe auch schon mit 'IOCtl()' eine nicht-blockierende Socket-Verbindung aufgebaut, was aber nichts gebracht hat (Der selbe Fehler).
Wüsste nicht mehr was ich noch machen soll. Das ist ja nicht das erste Mal, dass ich was mit Sockets mache, bisher hat es immer geklappt !?!
Bin ratlos, braucht Ihr etwas Code o.Ä.?
Mfg
Marco
-
Hm, ja, etwas Code wäre nicht schlecht.. Und könntest du Original-Bild und verzerrtes Bild (in guter Qualität) hochladen?
-
Kein Problem, hier das Bild: http://www.gayfiles.de/cpp/wouldblock.bmp (sah zwar schon verzerrter aus, aber man weis was ich damit meine).
Zum Code, hier die wichtigsten Funktionen:
Meine Receive() - Funktion:
void CCustomSocket::OnReceive(int iErrCode) { // Es werden Daten empfangen. if ((iErrCode == 0) && (wParent != NULL) && ((iSockMode == SM_CLIENT) || (iSockMode == SM_CONNECTION))) { CRecSockDataSet *rDataSetTmp; CObArray oDataSets; UINT uCount, uReceived; int iSizeUINT, iBufSizeTmp, iTemp; iSizeUINT = sizeof(UINT); // Anzahl Datensätze auslesen: CAsyncSocket::Receive(&uCount, iSizeUINT); // Daten empfangen: for (int i = 0; i < uCount; i ++) { rDataSetTmp = new CRecSockDataSet(); // Speichergröße auslesen: uReceived = CAsyncSocket::Receive(&rDataSetTmp->uSize, iSizeUINT); if (uReceived > 0) { // Speicher reservieren: rDataSetTmp->bBuffer = (BYTE*) malloc(rDataSetTmp->uSize); uReceived = 0; do { // Puffergröße berechnen: iBufSizeTmp = rDataSetTmp->uSize - uReceived; if (iBufSizeTmp > CSG_SOCK_BUFFER) // Puffergröße korregieren: iBufSizeTmp = CSG_SOCK_BUFFER; // Daten auslesen: iTemp = CAsyncSocket::Receive((rDataSetTmp->bBuffer + uReceived), iBufSizeTmp); if (iTemp == SOCKET_ERROR) { // Ein Fehler beim Lesen der Socket-Daten ist aufgetreten: wParent->SendMessage(CSWM_RECV_ERROR, (WPARAM) this, (LPARAM) GetLastError()); return; } else // Daten wurden korrekt ausgelesen: uReceived += iTemp; } while ((iTemp > 0) && (uReceived < rDataSetTmp->uSize)); } if (uReceived > 0) // Datensatz aufnehmen: oDataSets.Add(rDataSetTmp); else { // Datensatz löschen: delete rDataSetTmp; // Auslesen abbrechen: break; } } // Parent informieren wenn keine Fehler aufgetreten sind: if ((oDataSets.GetCount() > 0) && (wParent->SendMessage(CSWM_RECEIVE, (WPARAM) this, (LPARAM) &oDataSets) == 0)) { // Die ausgelesenen Datensätze verwerfen: for (INT_PTR i = oDataSets.GetCount() - 1; i >= 0; i --) delete oDataSets.GetAt(i); } } }Ich Sende Zu erst die Anzahl an "Datensätzen", welche ich dann innerhalb von 'OnReceive()' auslese.
Meine Funktion zum Starten des Servers / Clients:
BOOL CCustomSocket::CreateSocket(CString cServerAdr, int iPort) { BOOL bReturnVal = FALSE; if (iSockMode == -1) { // Der Socket wird nocht nicht verwendet und kann als Server / Client verwendet werden. // Socket zur Sicherheit vorher schließen (falls bereits geöffnet): CAsyncSocket::Close(); if ((iPort >= 0) && (cServerAdr == "")) { // Socket im Server-Modus erstellen: if ((CAsyncSocket::Create(iPort)) && (CAsyncSocket::Listen())) { iSockMode = SM_SERVER; bReturnVal = TRUE; // Zeitpunkt des Öffnen des Servers speichern: oConnTime = COleDateTime::GetCurrentTime(); } } else if ((iPort > 0) && (cServerAdr != "")) { // Socket im Client-Modus erstellen: if (CAsyncSocket::Create()) { // Verbindung aufbauen: CAsyncSocket::Connect(cServerAdr, iPort); bReturnVal = TRUE; } } } return (bReturnVal); }Ich glaube aber nicht, dass das Problem an diesen Funktionen liegt.
Naja, hoffe Ihr wisst nen Rat
Mfg
Marco
-
Du hast ja gar keine vernuenftige Fehlerueberpruefung drin! 3 Receive Aufrufe und nur einmal ein Test auf SOCKET_ERROR. Kein WSAEWOULDBLOCK in deinem Code zu sehen.
Man sollte im OnReceive-Handler auch nur einmal Receive aufrufen. Wenn noch mehr Daten verfuegbar sind wird OnReceive automatisch nochmal aufgerufen.
-
hmm...
Du hast Recht. Da probiere ich nochmal aus. Melde mich dann nochmal

-
Innerhalb von OnReceive() kannst du Receive() doch so oft aufrufen wie du wilst !? Wenn keine Daten mehr zum Auslesen existieren teilt einem das die Funktion ja mit.
Ich habe jetzt bei jedem Aufruf von Receive() auf SOCKET_ERROR geprüft. Er liefert mir sofort WSAEWOULDBLOCK (10035) zurück.
Ich hatte beim Erstellen meiner Testanwendung vergessen einzustellen, dass ich Sockets benutze. Ich habe nachträglich 'afxsock.h' included und 'AfxSocketInit()' aufgerufen. Hab ich da was vergessen?
Sonst fällt mir nichts mehr ein -.-
-
Ich habe jetzt die Ursache des Problems gefunden:
Zuerst einmal ist es ganz hilfreich den Unterschied zwischen CSocket und CAsyncSocket nachvollziehen zu können (http://www.c-plusplus.net/forum/viewtopic-var-p-is-23800.html)

Dann habe ich noch ein bischen bei google gesucht. Dort bin ich auf einen Beitrag gestossen indem geschrieben wurde, dass dieses Problem anscheinend nur beim Senden von größeren Datenmengen auftritt.
Getestet ... ist bei mir auch der Fall.MSDN:
WSAEWOULDBLOCK
A non-blocking socket operation could not be completed immediately.Jetzt weis ich auch was man mit dieser Erklärung anfanen soll:
Der Non-Blocking Socket empfängt Daten wenn OnReceive() aufgerufen wird. Ruft man dann sofort Receive() auf, kann es bei großen Datenmengen vorkommen, dass noch nicht alles vom Socket ausgelesen wurden >> Er ist also noch nicht bereit und würde blockieren wenn ein ein Blocking-Socket wäre.Ich habe einfach mal ein 'Sleep(700)' vor mein Receive gesetzt und es funktioniert. Das was ich da gerade erzählt habe stimmt also

Die Umgehung (!!) des Problems durch Sleep() ist ja eine ziemlich miese Lösung ^^ Hat da jemand einen eleganteren Ausweg?
Gruß
Marco
-
Red Skall schrieb:
Die Umgehung (!!) des Problems durch Sleep() ist ja eine ziemlich miese Lösung ^^ Hat da jemand einen eleganteren Ausweg?
Wenigstens etwas besser: Blockiere selber
Einfach eine Schleife, die läuft, solange Receive WSAE_WOULDBLOCK zurückgibt. Im Schleifenrumpf evtl ein Mini-Sleep, um 100% Prozessor-Auslastung zu vermeiden.
Oder gleich blockierende Sockets verwenden
-
Nachdem Receive WSAWOULDBLOCK liefert einfach die Funktion verlassen. OnReceive wird doch wieder aufgerufen wenn neue Daten kommen.