C++ Konsolenchat



  • Hi zur Zeit versuche ich ein Programm zu schreiben ,welches Nachrichten zu einem Server versendet. Dabei erstelle ich zur Laufzeit ein char array , damit ich auch strings versenden kann. Leider kommt bei meiner Serversoftware keine Nachricht an. xD

    Das ist der Code für den Client

    //---------------------------------------------------------------------------
    #include <iostream>
    #include <windows.h>
    #include <winsock2.h>
    #include <conio.h>
    #include <string>
    //---------------------------------------------------------------------------
    
    using namespace std;
    
    int wsastart(void);
    int s;
    struct sockaddr_in addr;
    long rc;
    string sbuf;
    char *pibuf;
    
    int main()
    {
    	rc=wsastart();
    	if (rc!=0)
    	{
    	  cout << "Fehler beim Erstellen des Sockets";
    	  getch();
    	  return 0;
    	}
    	s = socket(AF_INET, SOCK_STREAM, 0);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addr.sin_port = htons(1337);
    
    	rc=connect(s,(SOCKADDR*)&addr, sizeof(sockaddr_in));
    	if (rc!=0)
    	{
    	  cout << "Fehler beim Verbinden!";
    	  getch();
    	  return 0;
    	}
    	cout << "Nachricht: ";
    	cin >> sbuf;
    	while (sbuf!="/quit")
    	{
    	  pibuf = new char [sbuf.size()];
    	  send(s, pibuf, sizeof(pibuf), 0);
    	  delete[] pibuf;
    	  cout << "Nachricht: ";
    	  cin >> sbuf;
    	}
    	return 0;
    }
    
    int wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2),&wsa);
    }
    

    Und das ist der Code für den Server

    #include <windows.h>
    #include <winsock2.h>
    #include <iostream>
    #include <string>
    #include <conio.h>
    
    int wsastart(void);
    int rc;
    int s1;
    int s2;
    struct sockaddr_in addr;
    char pibuf[1024];
    
    int main()
    {
    	rc=wsastart();
    	if (rc!=0)
    	{
    	  std::cout << "Fehler beim starten des Winsocks!\n";
    	}
    //------------------------------------------------------------------------------
    	s1=socket(AF_INET,SOCK_STREAM,0);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = ADDR_ANY;
    	addr.sin_port = htons(1337);
    //------------------------------------------------------------------------------
    	rc=bind(s1, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit bind()   :" << WSAGetLastError() << std::endl;
    	}
    	rc=listen(s1, 1);
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit listen() :" << WSAGetLastError() << std::endl;
    	}
    	s2=accept(s1, NULL, NULL);
    	while( pibuf!="/close")
    	{
    	  recv(s2, pibuf, 1024, 0);
    	  std::cout << pibuf;
    	}
    	getch();
    	return 0;
    }
    
    int wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2), &wsa);
    }
    

    Also der Client scheint zu funktionieren. Da der Server keinen Text ausgibt geh ich davon aus ,das es da irgendwelche Probleme geben muss. Ich hoffe ihr könnt euch die Zeit nehmen um mal einen Blick darauf zu werfen und mir zu helfen. 🙂



  • send(s, pibuf, sizeof(pibuf), 0);
    

    Wird sizeof() nicht nur einmalig zur Compile-Zeit ausgewertet? Ich hätte da jetzt strlen()+1 erwartet...

    recv(s2, pibuf, 1024, 0);
    

    Bitte beachten das "HALLO\0" auch in mehreren Stücken z.B. ("HA", "L", "LO\0") ankommen kann. Also pro send() können mehrere recv() notwendig sein.



  • Ok das mit dem sizeof is mir klar.
    Aber woran seh ich denn wieviel recv() ich brauch?
    Zudem kommt auch nix an wenn ich nur einen buchstaben versende



  • luggas schrieb:

    Aber woran seh ich denn wieviel recv() ich brauch?

    Oft sendet man einfach das String-Ende-Zeichen ('\0') oder einen Zeilenumbruch ('\n') mit, auf Empfängerseite ruft man dann so oft recv() auf bis das '\0' dabei ist.
    "HALLO" sieht im RAM quasi so aus: {'H',A','L','L','O','\0')
    strlen("HALLO") ergibt 5, strlen("HALLO")+1 ergibt 6.
    send(s,"HALLO",strlen("HALLO")+1,0) würde also das '\0'-Zeichen mitsenden.

    Auf Empfängerseite kann man dann z.B. sowas machen (Pseudocode):

    string nachricht=""; // Hier speichern wir unsere Nachricht rein
    bool nachrichtKomplett=false;
    
    // So oft recv() aufrufen bis unsere nachricht komplett ist:
    while (!nachrichtKomplett)
    {
       char zeichen;
    
       // Immer nur maximal 1 Zeichen abholen:
       int bytesErhalten=recv(s, &zeichen, 1, 0);
       if (bytesErhalten==SOCKET_ERROR)
       {
           // Mist, irgendnen Fehler ist aufgetreten
       }
       else if (bytesErhalten==0)
       {
           // Verbindung wurde geschlossen
       }
       else if (bytesErhalten>0)
       {
          // neu gelesenes Zeichen an nachricht dranhängen:
          nachricht=nachricht+zeichen;
          if (zeichen=='\0')
          {
              // Wir haben gerade das '\0'-Zeichen bekommen ;D
              nachrichtKomplett=true;
          }
       }   
    }
    

    ...recv() mit nem 1-Byte Puffer aufzurufen ist zwar ziemlich ineffizient, macht die Sache aber einfacher 😉

    -----

    Pack erstmal eine Ausgabe ("Client ist verbunden" oder so) hinter accept() um zu sehen ob die Verbindung beim Server überhaupt ankommt...



  • Ok mach ich mal danke



  • So ich habs jetzt mal versucht und jetzt sieht es so aus im servercode

    #include <windows.h>
    #include <winsock2.h>
    #include <iostream>
    #include <conio.h>
    #include <string>
    
    int wsastart(void);
    int rc;
    int s1;
    int s2;
    struct sockaddr_in addr;
    char c;
    bool nc;
    std::string smessage;
    
    int main()
    {
    	rc=wsastart();
    	if (rc!=0)
    	{
    	  std::cout << "Fehler beim starten des Winsocks!\n";
    	}
    //------------------------------------------------------------------------------
    	s1=socket(AF_INET,SOCK_STREAM,0);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = ADDR_ANY;
    	addr.sin_port = htons(1337);
    //------------------------------------------------------------------------------
    	rc=bind(s1, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit bind()   :" << WSAGetLastError() << std::endl;
    	}
    	rc=listen(s1, 1);
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit listen() :" << WSAGetLastError() << std::endl;
    	}
    	s2=accept(s1, NULL, NULL);
    	if (s2==-1)
    	{
    	  std::cout << "client nicht verbunden!";
    	}
    	while( smessage!="/close")
    	{
    	  nc=false;
    	  smessage="";
    	  while(nc!=true)
    	  {
    		rc=recv(s2, &c, 1, 0);
    		if (rc>0)
    		{
    		  smessage=smessage+c;
    		  if (c=='\0')
    		  {
    			nc=true;
    		  }
    		}
    	  }
    	  std::cout << smessage;
    	}
    	getch();
    	return 0;
    }
    
    int wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2), &wsa);
    }
    

    nur leider kommt immer noch nix an

    ps.: der client hat ne verbindung



  • Warum willst du denn so viele globale Variablen verwenden?



  • Die haben doch den Vorteil, dass man von überall darauf Zugriff hat!


  • Mod

    Die haben doch den Vorteil Nachteil, dass man von überall darauf Zugriff hat!



  • 😉



  • das mach ich erst einmal immer. sprich bevor der code fertig ist. zur übersichtlichkeit 🙂



  • An globalen Variablen ist nichts übersichtlich, ich würde eher sagen im Gegenteil.



  • da bin ich anderer Meinung

    is ja auch jedem selbst überlassen wie er es für übersichtlich hält



  • Nicht ohne Grund sind sie verpönt 😉
    Wenn du irgendwo auf einen Namen zugreifst, der im momentanen Scope nicht vorhanden ist, frage ich mich zumindest mal, wo das Ding herkommt. Ich denke ich bin da nicht alleine.

    Wo gewinnst du denn dadurch Übersicht?


  • Mod

    Grundsätzlicher Rat: Definiere eine Variable so nahe wie möglich an der Nutzung und immer im engsten Scope der möglich ist.
    Globale Variablen sprechen gegen beide Prämissen!



  • Ok werd ich mir merken.
    Ich werd mal probiern beim Client das '\0' mit zu verschicken 🙂



  • So ich hab jetzt mal bisschen was um geschrieben leider funktioniert es immer noch nicht 😕

    Client

    //------------------------------------------------------------------------------
    #include <iostream>
    #include <windows.h>
    #include <winsock2.h>
    #include <conio.h>
    #include <string>
    //------------------------------------------------------------------------------
    
    using namespace std;
    
    int wsastart(void);
    
    int main()
    {
    /*
    ################################################################################
    			 VARIABLEN
    ################################################################################
    */
    	int s;
    	struct sockaddr_in addr;
    	long rc;
    	string sbuf;
    	char c;
    	int i;
    //##############################################################################
    
    /*
    ################################################################################
    			 SOCKET INITIALISIERUNG & VERBINDUNGSAUFBAU
    ################################################################################
    */
    
    	rc=wsastart();
    	if (rc!=0)
    	{
    	  cout << "Fehler beim Erstellen des Sockets";
    	  getch();
    	  return 0;
    	}
    	s = socket(AF_INET, SOCK_STREAM, 0);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addr.sin_port = htons(1337);
    
    	rc=connect(s,(SOCKADDR*)&addr, sizeof(sockaddr_in));
    	if (rc!=0)
    	{
    	  cout << "Fehler beim Verbinden!";
    	  getch();
    	  return 0;
    	}
    //##############################################################################
    
    /*
    ################################################################################
    			 VERSENDEN VON STRINGS STÜCKCHENWEISE
    ################################################################################
    */
    	cout << "Nachricht: ";
    	cin >> sbuf;
    	do
    	{
    	  sbuf[sbuf.length()+1]='\0';
    	  for (i = 0; i < sizeof(sbuf); i++)
    	  {
    	  c=sbuf[i];
    	  rc=send(s, &c, 1, 0);
    	  if (rc==-1)
    	  {
    		printf("\nSend ist fehlgeschlagen!");
    		getch();
    		return 0;
    	  }
    	  }
    	  cout << "Nachricht: ";
    	  cin >> sbuf;
    	}while (sbuf!="/quit");
    //##############################################################################
    	return 0;
    }
    
    int wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2),&wsa);
    }
    

    Der Server

    #include <windows.h>
    #include <winsock2.h>
    #include <iostream>
    #include <conio.h>
    #include <string>
    #include <stdio.h>
    
    int wsastart(void);
    
    int main()
    {
    /*
    ################################################################################
    			 VARIABLEN
    ################################################################################
    */
    	int rc;
    	int s1;
    	int s2;
    	struct sockaddr_in addr;
    	std::string smessage;
    //##############################################################################
    
    /*
    ################################################################################
    			 SOCKET INITIALISIERUNG & WARTEN AUF CLIENT
    ################################################################################
    */
    	rc=wsastart();
    	if (rc!=0)
    	{
    	  std::cout << "Fehler beim starten des Winsocks!\n";
    	}
    //------------------------------------------------------------------------------
    	s1=socket(AF_INET,SOCK_STREAM,0);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = ADDR_ANY;
    	addr.sin_port = htons(1337);
    //------------------------------------------------------------------------------
    	rc=bind(s1, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit bind()   :" << WSAGetLastError() << std::endl;
    	}
    	rc=listen(s1, 1);
    	if (rc!=0)
    	{
    	  std::cout << "Fehler mit listen() :" << WSAGetLastError() << std::endl;
    	}
    //##############################################################################
    
    /*
    ################################################################################
    			 VERBINDUNG ANNEHMEN & EMPFANGEN VON STRINGS STÜCKCHENWEISE
    ################################################################################
    */
    	s2=accept(s1, NULL, NULL);
    	if (s2==-1)
    	{
    	  std::cout << "client nicht verbunden!";
    	}
    	smessage="";
    	do
    	{
    	  bool nc=false;
    	  int i=0;
    	  char c;
    	  do
    	  {
    		  recv(s2, &c, 1, 0);
    		  smessage[i]=c;
    		  if (smessage[i]=='\0')
    		  {
    			nc=true;
    		  }
    	   i++;
    	  }while(nc!=true);
    	  std::cout << smessage;
    	}while( smessage!="/close");
    //##############################################################################
    	return 0;
    }
    
    int wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2), &wsa);
    }
    

    Vielleicht hat noch einer ne Idee 😃



  • Ich gehe hier mal nun auf den Client ein, denn dein Code ist schrecklich. Ich empfehle dir, eine Socketklasse zu erstellen, außerdem solltest du bei Fehlern eine Exception schmeißen. Das macht dein Programm viel besser wartbar.

    Hier mal ein Vorschlag. Habs mit Absicht simpel gehalten. Die main-Funktion hab ich mal großzügig weggelassen.

    namespace luggashatsgemacht
    {
        struct SocketError : std::runtime_error
        {
            SocketError(const std::string& msg)
                : std::runtime_error(msg)
            {}
        };
    
        struct WSAGuard
        {
            WSAData wsa;
            bool success;
    
            void dummy(){}
    
        public:
            typedef void (WSAGuard::* bool_type) ();
    
            WSAGuard()
            {
                success = !WSAStartup(MAKEWORD(2, 2), &wsa);
            }
    
            operator bool_type()
            {
                return success ? &WSAGuard::dummy : 0;
            }
    
            ~WSAGuard()
            {
                WSACleanup();
            }
        };
    
        class Socket
        {
            int sockfd;
    
            static WSAGuard::bool_type guard();
    
            Socket(const std::string& host, unsigned short port);
           ~Socket();
    
            std::string read();
            void write(const std::string&);
        };
    }
    
    luggashatsgemacht::WSAGuard::bool_type luggashatsgemacht::guard()
    {
        static WSAGuard guard;
        return guard;
    }
    
    luggashatsgemacht::Socket::Socket(const std::string& ip, unsigned short port)
    {
        if(!guard())
            throw SocketError("winsock couldn't be initialized!");
    
        if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
            throw SocketError("socket couldn't be created!");
    
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_port = htons(port);
    
        if(connect(sockfd, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr))
            throw SocketError("failed to connect!");
    }
    
    luggashatsgemacht::Socket::~Socket()
    {
        closesocket(sockfd);
    }
    
    std::string luggashatsgemacht::Socket::read()
    {
        char buf[256];
    
        int n = recv(sockfd, buf, sizeof(buf) - 1, 0);
    
        if(n == -1)
            throw SocketError("failed to read from socket!");
    
        return std::string(buf, buf + n);
    }
    
    void luggashastgemacht::Socket::write(const std::string& s)
    {
        if(send(sockfd, s.c_str(), s.size(), 0) == -1)
            throw SocketError("failed to write to socket!");
    }
    

    Selbstverständlich ohne Gewähr und ungetestet. Meiner Meinung nach wäre es aber am einfachsten, direkt boost::asio zu verwenden.



  • Hallo schau mal hier das sollte dir helfen:

    http://www.c-worker.ch/tuts.php



  • Danke erstmal für die beiden Antworten. 👍

    Wobei ich noch nie mit der boost lib gearbeitet habe werd ich erst mal über den Rückgabewert von recv() gehen ^^


Anmelden zum Antworten