[Client-Server] Problem mit recv (und send?)
-
hi leute,
habe mir eine MFC-Anwendung (Client-Server, TCP) gebastelt, gebe dann im Client in eine Listbox meine Nachricht ein und kann per klicken auf den Senden-Button diese Nachricht an den Server schicken, der diese dann in einer Listbox ausgibt.
Ich habe bloß noch ein Problem mit dem Empfangen bzw. Anzeigen der Nachricht beim Server, in buffer steht meistens nur Mist drin (außer ich gebe z.B. 2 Buchstaben wie gh ein, dann passt das).

Also das Send beim Client (was eigentlich soweit passen müsste !?):
void CClientDlg::OnBnClickedSend() { BOOL status; CEdit *pEdit = static_cast<CEdit*>(GetDlgItem(IDC_EDIT1)); status = clientsock.Connect("localhost", "12000"); if (status == FALSE) { logList.AddString("Server offline..."); } else { pEdit->GetWindowText(strEdit); if (pEdit->LineLength() == 0) { logList.AddString("Keine Daten zum Senden..."); } else { strEdit += '\0'; clientsock.Send(strEdit, strEdit.GetLength()); logList.AddString("Daten wurden gesendet"); } } } }und das Empfangen beim Server (läuft in einem Thread):
void CServerDlg::ThreadRun() { long bytesRecv; std::string message; while (true) { fd_set rfds; if(serversock.Select(rfds)) { SOCKET fd = serversock.GetSocket(); if( serversock.IsConnected() && FD_ISSET(fd, &rfds) ) { if(ClientSock.Accept(fd)) { /*if (!ClientSock.ChkRecv(buffer, sizeof(buffer)))*/ char *tempBuf = new char[50]; bytesRecv = recv(ClientSock.GetSocket(), tempBuf, sizeof(tempBuf), 0); if (bytesRecv == 0) { logList.AddString("Client offline..."); logList.SetCurSel(1); break; } if (bytesRecv < 0) { logList.AddString("Fehler bei recv()..."); logList.SetCurSel(1); break; } if (bytesRecv > 0) { for (int i = 0; i <= bytesRecv; i++) message += tempBuf[i]; strList.AddString(message.c_str()); } delete[] tempBuf; } } closesocket(fd); } } } }(Die Funktionen Select, IsConnected und Accept sind auch aus dieser DLL)
und dann noch OnClose() vom Server (sieht beim Client ähnlich aus):
void CServerDlg::OnClose() { ClientSock.Close(); AfxEndThread(0); CloseSocket(); // WSACleanup() serversock.Close(); CDialog::OnClose(); }Dann weiß ich noch net genau, wie ich das machen soll, dass ich beim Client in die Listbox eine mehrzeilige Nachricht eingebe und diese dann im Server ausgebe !?
Außerdem kommt beim Client "Server offline", wenn ich ein 2. Mal auf Senden klicke.Wäre super, wenn mir jemand dabei Hilfe geben könnte !
-
kann mir wirklich keiner helfen ??
Es wäre aber schön

-
Was mir auffällt: select in Threads könnte Probleme machen.
Ausserdem
char *tempBuf = new char[50]; bytesRecv = recv(ClientSock.GetSocket(), tempBuf, sizeof(tempBuf), 0);sizeof(tempBuf) ist die Größe des Zeigers (wahrscheinlich 4 Byte) und nicht die des reservierten Speichers.
-
Ich hatte die Idee, dass ich nach dem Start vom Server nen Thread starte, der eben select ausführt, und den ganzen Spaß wie accept... realisiert und dass eben solange der Server läuft (deswegen ja die Schleife).
Ich hab nicht wirklich ne andere Idee, wie ich das sonst noch lösen kann !?
und dass mit dem sizeof(tempBuf) ist mir noch nicht wirklich klar, aber wenn ich die Größe des Speichers erhalten will, auf den der Zeiger verweist, dann bedeutet doch sizeof(tempbuf) = Größe des Speichers, auf den der Pointer zeigt, oder ??
-
sizeof liefert die Größe des Datentyps (ist vielleicht nicht das richtige Wort).
Es gibt keine Möglichkeit die Größe des Speichers zu erfragen.
Mach dir einfach mal ein kleines Programm und gib sizeof(irgendwas) aus.
sizeof gibt dir die zur compile Zeit bestimmbare Größe.
Was funktioniert ist:char buff[500]; sizeof(buff) -> 500 char* buff = new char[500]; sizeof(buff) -> Größe von char* Was du brauchst ist z.B. const size_t BUFF_SIZE = 500; char* buff = new char[BUFF_SIZE]; recv(...,buff,BUFF_SIZE,...)Zum Thema select: swoeit ich das weiss gibt es Probleme, wenn du ein select auf einen socket in mehreren Threads machst - da bin ich mir nicht 100% sicher.
Du könntest dann das accept in den Main-Thread legen und den Client-Socket an einen Thread übergeben (der dann select für diesen Client-Socket verwendet).
-
ok stimmt mit dem sizeof, sind so kleine Dinge, aber man lernt ja immer dazu

Vielen Dank erstmal dafür !Nuja, mit dem select weiß ich noch nicht so recht, wie ich das machen soll.
Ich bau also mein select in den Mainthread meiner Applikation, ok gut, aber ich weiß nicht wirklich, wo genau ich das machen soll, mein Gedankengang war, dass der Server mit select dauernd überprüft, ob ein neuer Client da ist, und dafür scheint mir ein zweiter Thread am sinnvollsten, da der ja in einer Schleife das dauernd prüft und der Thread eben die ganze Zeit läuft und das macht.ich weiß nicht genau, ob das Sinn ergibt, wenn ich meinen Server in z.B. OninitDialog auf select "setze", aber wo prüfe ich dann dauernd, ob neue Clients da sind ??
Ich hab da irgendwie noch nen Denkfehler, mit dem allgemeinen Aufbau meines Servers halt.

Ein Ansatz wäre ja, halt das select in OninitDialog und irgendwie prüfen, ob es neue Clients gibt und wenn ja, dann meine Threadfunktion ausführen, in der ich dann die Daten empfang und ausgebe.
Hab bloß keinen Schimmer, ob das so hinhauen könnte und wenn ja, wie das dann umsetze !
Achja: bin jetzt erstmal soweit (habe den Aufbau bis jetzt erstmal so gelassen):
Servervoid CServerDlg::ThreadRun() { SOCKET fd = serversock.GetSocket(); const size_t BUFFSIZ = 255; while (true) { std::string message; int index; fd_set rfds; if(serversock.Select(rfds)) { if( serversock.IsConnected() && FD_ISSET(fd, &rfds) ) { if(ClientSock.Accept(fd)) { char *tempBuf = new char[]; if ( !ClientSock.ChkRecv(tempBuf, BUFFSIZ) ) { index = logList.AddString("Fehler beim Empfangen..."); logList.SetCurSel(index); continue; } else if (strnlen(tempBuf, BUFFSIZ) == 0) { index = logList.AddString("Keine Daten empfangen..."); logList.SetCurSel(index); continue; } else { for (int i = 0; i < BUFFSIZ; i++) message += tempBuf[i]; index = logList.AddString("Daten erhalten..."); logList.SetCurSel(index); strList.AddString(message.c_str()); } delete[] tempBuf; } } } } closesocket(fd); OnClose(); }Client
void CClientDlg::OnBnClickedSend() { BOOL status; int index, LineLen; CEdit *pEdit = static_cast<CEdit*>(GetDlgItem(IDC_EDIT1)); status = clientsock.Connect("localhost", "12000"); if (status == FALSE) { index = logList.AddString("Server offline..."); logList.SetCurSel(index); } else { if (pEdit->LineLength() == 0) { index = logList.AddString("Keine Daten zum Senden..."); logList.SetCurSel(index); } else { // und hier versuche(!) ich noch, das alles, was im Editfeld steht, zu senden // da Editfeld ja multiline ist LPTSTR bufLine = new char[255]; for (int i = 0; i < pEdit->GetLineCount(); i++) { LineLen = ctEdit.LineLength(ctEdit.LineIndex(i)); ctEdit.GetLine(i, bufLine, LineLen); strEdit.Append(bufLine); clientsock.Send(strEdit, strEdit.GetLength()); } delete[] bufLine; /*strEdit += '\0';*/ index = logList.AddString("Daten wurden gesendet"); logList.SetCurSel(index); } } }Ich hoffe, ich hab hier etwas verständlich geschrieben !?

-
Äh ich glaube wir reden aneinander vorbei...?
Zu select: das bezog sich nur darauf, wenn du den *selben* sockent in mehreren Threads mit select überwachst, könnte es Probleme geben.
Hast du aber genau einen Thread der mit select arbeitet gibt es kein Problem.Ich dachte du hast mehrere Threads die alle ein select auf *ein und denselben* Socket machen (den "Server"-Socket, der der zum Annehmen der Clients verwendet wird).
Fehlt da nicht ein FD_SET (FD_SET(fd,rfds))? Oder macht das die Methode serversock.Select?
SOCKET fd = serversock.GetSocket(); const size_t BUFFSIZ = 255; while (true) { std::string message; int index; fd_set rfds; if(serversock.Select(rfds))
-
achso, nein, hab mich da etwas unglücklich ausgedrückt, ich habe bloß einen zweiten Thread, der das select ausführt, sonst keinen Anderen.
nee, des FD_SET macht das select in der DLL, das passt (hätte ich auch eher sagen sollen, sry
)ok, habe jetzt nur noch das Problem mit dem kompletten Auslesen des Editfeldes und wenn dann alles habe, dann wird der komplette Inhalt an den Server gesendet.
will in der for-Schleife solange das Feld auslesen, bis ich alles habe und die jeweilige Zeile in jedem Durchlauf in strEdit (ist CString) schmeißen und das dann eben sendenund ich tue mich im Server noch etwas schwer, einen Puffer zu erstellen, der genauso so groß ist, wie die Nachricht selber (die meinetwegen vorher in einen ausreichend großen Temp-Puffer kopiert wurde, oder so in der Art)
bin eben noch am Überlegen, was ich besser machen kann oder was gar nicht hinhaut von dem, was ich produziert habe
