Chatprogramm aus C in C++ wo liegt mein Denkfehler?
-
Ich mache gerade ein Tutorial über Sockets in dem ich ein Chatprogramm programmiere, da das ganze in C gehalten ist dachte ich mir das es eine gute Übung wär den Code in C++ (also Objektorientiert) zu gestalten um auch gleichzeitig das logische Verständnis zu erweitern.
Allerdings bin ich gerade bei einem Fehler wo es partou nicht weiter geht.Im Tutorial wird gesagt das man den main header mit (int argc, char** argv) erweitern soll damit man das programm später dann mit dem argument "localhost" aufrufen kann. Ich will aber das dieses finden des Hosts in der Klasse von stattengeht und nix mit dem aufruf der Main zu tun hat.
Nun aber da tritt nun ein Fehler auf:
#pragma comment( lib, "ws2_32.lib" ) #include <winsock2.h> #include <windows.h> #include <cstdio> #include <string> int startWinsock(void); long getAddrFromString(char* hostnameOrIp, SOCKADDR_IN* addr); class Connection { long rc; SOCKET s; SOCKADDR_IN addr; char buf[256]; bool anyerror; char** connect_to; //zum Speichern des char** arguments das eigentlich im main header ist public: Connection(char** connect_to_ui) // zum immitieren des Main aufrufes { rc = startWinsock(); bool anyerror = true; bool winsock_err = true; bool socket_err = true; connect_to = connect_to_ui; if(rc!=0) printf("Fehler: startWinsock, fehler code: %d\n",rc); else { printf("Winsock gestartet!\n"); winsock_err = false; } s=socket(AF_INET,SOCK_STREAM,0); if(s==INVALID_SOCKET) printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError()); else { printf("Socket erstellt!\n"); socket_err = false; } if(!(socket_err + winsock_err)) { printf("Socket and Winsock successfull\n"); anyerror = false; } else printf("Errors occured\n"); } bool connect_to_name_or_ip() { memset(&addr,0,sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten addr.sin_family=AF_INET; addr.sin_port=htons(12345); // wir verwenden mal port 12345 //addr.sin_addr.s_addr=inet_addr("127.0.0.1"); // zielrechner ist unser eigener rc = get_Addr_From_String(connect_to[1]); //dieser Aufruf soll den aufruf im Tutorial simulieren und ist rein theoretisch auch der selbe... if(rc==SOCKET_ERROR) { printf("IP für %s konnte nicht aufgeloest werden\n", connect_to); anyerror = 0; } else { printf("IP aufgeloest!\n"); } rc=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)); if(rc==SOCKET_ERROR) { printf("Fehler: connect gescheitert, fehler code: %d\n",WSAGetLastError()); anyerror = true; system("PAUSE"); return false; } else printf("Verbunden mit 127.0.0.1..\n"); return true; } void speak() { while(rc!=SOCKET_ERROR) { printf("\nZeichenfolge eingeben [max 256]: "); gets(buf); send(s,buf,strlen(buf),0); rc=recv(s,buf,256,0); if(rc==0) { printf("Server hat die Verbindung getrennt..\n"); break; } if(rc==SOCKET_ERROR) { printf("Fehler: recv, fehler code: %d\n",WSAGetLastError()); break; } buf[rc]='\0'; printf("\nServer antwortet: %s\n",buf); } } long get_Addr_From_String(char* connect_to) { long rc; unsigned long ip; HOSTENT* he; /* Parameter prüfen */ if(connect_to==NULL || addr==NULL) // er sagt mir das das hier in unzulässiger Vergleich sei return SOCKET_ERROR; /* eine IP in hostnameOrIp ? */ ip=inet_addr(connect_to); /* bei einem fehler liefert inet_addr den Rückgabewert INADDR_NONE */ if(ip!=INADDR_NONE) { addr.sin_addr.s_addr=ip; return 0; } else { /* Hostname in hostnameOrIp auflösen */ he=gethostbyname(connect_to); if(he==NULL) { return SOCKET_ERROR; } else { /*die 4 Bytes der IP von he nach addr kopieren */ memcpy(&(addr.sin_addr),he->h_addr_list[0],4); } return 0; } } ~Connection() { closesocket(s); WSACleanup(); } }; int main() { Connection one("localhost"); if(one.connect_to_name_or_ip()) one.speak(); else printf("an error occured"); } system("PAUSE"); return 0; } int startWinsock(void) { WSADATA wsa; return WSAStartup(MAKEWORD(2,0),&wsa); }Der Originalcode der abgeänderten Methode ist:
/*#include <windows.h> #include <winsock2.h> #include <stdio.h> //Prototypen int startWinsock(void); long getAddrFromString(char* hostnameOrIp, SOCKADDR_IN* addr); int main(int argc, char** argv) { long rc; SOCKET s; SOCKADDR_IN addr; char buf[256]; if(argc<2) { printf("Usage: sock <hostname oder ip des servers>\n"); return 1; } // Winsock starten rc=startWinsock(); if(rc!=0) { printf("Fehler: startWinsock, fehler code: %d\n",rc); return 1; } else { printf("Winsock gestartet!\n"); } // Socket erstellen s=socket(AF_INET,SOCK_STREAM,0); if(s==INVALID_SOCKET) { printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError()); return 1; } else { printf("Socket erstellt!\n"); }*/ // Verbinden memset(&addr,0,sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten addr.sin_family=AF_INET; addr.sin_port=htons(12345); // wir verwenden mal port 12345 rc=getAddrFromString(argv[1],&addr); // HIER WIRD ES INTERRESSANT BEI DIESEM AUFRUF if(rc==SOCKET_ERROR) { printf("IP für %s konnte nicht aufgeloest werden\n", argv[1]); return 1; } else { printf("IP aufgeloest!\n"); } /* rc=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)); if(rc==SOCKET_ERROR) { printf("Fehler: connect gescheitert, fehler code: %d\n",WSAGetLastError()); return 1; } else { printf("Verbunden mit 127.0.0.1..\n"); } // Daten austauschen /* while(rc!=SOCKET_ERROR) { printf("\nZeichenfolge eingeben [max 256]: "); gets(buf); send(s,buf,strlen(buf),0); rc=recv(s,buf,256,0); if(rc==0) { printf("Server hat die Verbindung getrennt..\n"); break; } if(rc==SOCKET_ERROR) { printf("Fehler: recv, fehler code: %d\n",WSAGetLastError()); break; } buf[rc]='\0'; printf("\nServer antwortet: %s\n",buf); } closesocket(s); WSACleanup(); return 0; } //int startWinsock(void) //{ // WSADATA wsa; // return WSAStartup(MAKEWORD(2,0),&wsa); //} long getAddrFromString(char* hostnameOrIp, SOCKADDR_IN* addr) { long rc; unsigned long ip; HOSTENT* he; /* Parameter prüfen */ if(hostnameOrIp==NULL || addr==NULL) return SOCKET_ERROR; /* eine IP in hostnameOrIp ? */ ip=inet_addr(hostnameOrIp); /* bei einem fehler liefert inet_addr den Rückgabewert INADDR_NONE */ if(ip!=INADDR_NONE) { addr->sin_addr.s_addr=ip; return 0; } else { /* Hostname in hostnameOrIp auflösen */ he=gethostbyname(hostnameOrIp); if(he==NULL) { return SOCKET_ERROR; } else { /*die 4 Bytes der IP von he nach addr kopieren */ memcpy(&(addr->sin_addr),he->h_addr_list[0],4); } return 0; } } */Wobei den Code den ich als für das Problem unwichtig erachte auskommentiert habe
Das Tutorial ist: http://www.c-worker.ch/tuts.php
-
Ehm, verstehe ich richtig, dass dein Problem bei dieser Zeile liegt?
if(connect_to==NULL || addr==NULL) // er sagt mir das das hier in unzulässiger Vergleich sei return SOCKET_ERROR;addrist von TypSOCKADDR_IN. Im Tutorial istaddrvom TypSOCKADDR_IN*. Bei dir ist es eine Struktur, welche man nicht auf NULL überprüfen kann und im Tutorial ist es ein Zeiger auf eine Struktur, welche man natürlich auf NULL überprüfen kann. Das sind Grundlagen. Da fragt man sich fast, ob du den ursprünglichen Code auch vollständig verstanden hast. Zum Beispiel: Warum diese Prüfung auf NULL durchgeführt wird.Auch ist deine Portierung alles andere als gelungen. Du hast ein typisches C mit Klassen geschaffen, was nicht einem gutem C++ Design entspricht.
Grüssli
-
Nja so wie ich das sehe wir die überprüfung auf Null deswegen vorgenommen um sicher zu gehen das man nicht mit leeren Zeigern Arbeitet, was später auf jeden Fall zu Fehlern/Fehlverhalten führen würde.
Ah, ich kann eigentlich den Vergleihc für addr auch einfach weglassen da das ja garantiert existiert.Ich bin immer offen für Kritik. Das meine Portierung nicht sehr gelungen ist kann sein nur meine Erfahrungen was das angeht sind begrenzt von daher wären präzise Fehlererleuterungen hilfreicher als ein "nicht gut"
-
Anstatt den char** Parameter von main() an deine Klasse weiter zu geben, wäre es deutlich sinnvoller diesen Parameter in der main()-Funktion selbst zu filtern. Deiner Klasse übergibst du dann nur noch den konkreten Host (als std::string).
-
mit strings zu arbeiten kann man total vergessen :S, ich habe es kurz probiert und alle benutzen Methoden haben mir Fehlercodes ausgegeben "Char* expected, no memberfunktion fpr std::string" oder so ähnlich. Ich mag diese Char arrays selber nicht gerne aber ich habe keine alternative Lösung gefunden.
Wo wir schon am fragen sind^^, wofür ist long rc eigentlich genau da? Es wird im Tutorial nicht erwähnt.
-
Dann lerne std::string richtig zu benutzen, das gehört absolut zu C++ dazu und wird dir SEHR helfen.
-
Dann lass das lieber mit den Sockets und schau dir noch mal etwas über C++ und std::string an.
-
Jud4s schrieb:
mit strings zu arbeiten kann man total vergessen :S, ich habe es kurz probiert und alle benutzen Methoden haben mir Fehlercodes ausgegeben "Char* expected, no memberfunktion fpr std::string" oder so ähnlich. Ich mag diese Char arrays selber nicht gerne aber ich habe keine alternative Lösung gefunden.
Wo wir schon am fragen sind^^, wofür ist long rc eigentlich genau da? Es wird im Tutorial nicht erwähnt.
Du bekommst eine char*-Repräsentation eines std::strings mit der Memberfunktion .c_str().
rc enthält den Rückgabewert von WSAStartup (0 wenn's erfolgreich war). Das hat also in der Klasse nichts zu suchen, der Aufruf von WSAStartup/-Cleanup auch nicht. Diese Funktionen sollten jeweils einmal bei Programmstart und -ende aufgerufen werden.
-
Ahh danke, das war hilfreich

@Athar, manche funktionen wie z.b. recv() fordern ein char* , .c_str() gibt ein const char* zurück. Kann man das manipulieren?
Hmmm werd wohl in den sauren Apfel beißen und erstmal mein C++ Wissen vertiefen schade eigtl. Das lernen zieht sich ohne das ich etwas programmiert habe mit dem ich was anfangen kann.