[Anfänger] Sockets unter Linux



  • Hallo,

    ich habe ein (großes) Problem bei der Verwendung von Sockets mit C++ und Linux. Meine Distribution ist Debian GNU/Linux, falls das eine Rolle spielt. Als IDE benutze ich Code::Blocks.
    Folgenden Code habe ich aus dem Buch C++ von A bis Z, welchen ich eigentlich auch soweit verstanden habe. Leider schaffe ich es aber nicht das Programm auszuführen.

    socket.h

    #ifndef SOCKET_H_
    #define SOCKET_H_
    #include <string>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    using namespace std;
    
    // Max. Anzahl Verbindungen
    const int MAXCONNECTIONS = 5;
    // Max. Anzahl an Daten die aufeinmal empfangen werden
    const int MAXRECV = 1024;
    
    // Die Klasse Socket
    class Socket {
       private:
       // Socketnummer (Socket-Deskriptor)           
       int m_sock; 
       // Struktur sockaddr_in
       sockaddr_in m_addr;
    
       public:
       // Konstruktor
       Socket();
       // virtueller Destruktor
       virtual ~Socket();
    
       // Socket erstellen - TCP
       bool create();
       // Socket erstellen - UDP
       bool UDP_create();
       bool bind( const int port );
       bool listen() const;
       bool accept( Socket& ) const;
       bool connect ( const string host, const int port );
       // Datenuebertragung - TCP
       bool send ( const string ) const;
       int recv ( string& ) const;
       // Datenuebertragung - UDP
       bool UDP_send( const string, const string,
                      const int port ) const;
       int UDP_recv( string& ) const;
       // Socket schliessen
       bool close() const;
       // WSAcleanup()
       void cleanup() const;
       bool is_valid() const { return m_sock != -1; }
    };
    #endif
    

    socket.cpp

    // socket.cpp
    #include <cstdlib>
    #include <iostream>
    #include "socket.h"
    using namespace std;
    
    // Konstruktor
    Socket::Socket() : m_sock(0) { }
    
    // Destruktor
    Socket::~Socket() {
       if ( is_valid() )
          ::close( m_sock );
    }
    
    // Erzeugt das Socket  - TCP
    bool Socket::create() {
       m_sock = ::socket(AF_INET,SOCK_STREAM,0);
       if (m_sock < 0) {
          cout << "Fehler beim Anlegen eines Socket" << endl;
          exit(1);
       }
       int y=1;
       // Mehr dazu siehe Hinweis am Ende
       setsockopt( m_sock, SOL_SOCKET,
                   SO_REUSEADDR, &y, sizeof(int));   
       return true;
    }
    
    // Erzeugt das Socket  - UDP
    bool Socket::UDP_create() {
       m_sock = ::socket(AF_INET,SOCK_DGRAM,0);
       if (m_sock < 0) {
          cout << "Fehler beim Anlegen eines Socket" << endl;
          exit(1);
       }
    }
    
    // Erzeugt die Bindung an die Serveradresse
    // - genauer an einen bestimmten Port
    bool Socket::bind( const int port ) {
       if ( ! is_valid() ) {
          return false;
       }
       m_addr.sin_family = AF_INET;
       m_addr.sin_addr.s_addr = INADDR_ANY;
       m_addr.sin_port = htons ( port );
    
       int bind_return = ::bind ( m_sock,
          ( struct sockaddr * ) &m_addr, sizeof ( m_addr ) );
       if ( bind_return == -1 ) {
          return false;
       }
       return true;     
    }
    
    // Teile dem Socket mit, dass Verbindungswünsche
    // von Clients entgegengenommen werden
    bool Socket::listen() const {
       if ( ! is_valid() ) {
          return false;
       }
       int listen_return = ::listen ( m_sock, MAXCONNECTIONS );
       if ( listen_return == -1 ) {
          return false;
       }
      return true;
    }
    
    // Bearbeite die Verbindungswuensche von Clients
    // Der Aufruf von accept() blockiert solange,
    // bis ein Client Verbindung aufnimmt
    bool Socket::accept ( Socket& new_socket ) const {
       int addr_length = sizeof ( m_addr );
       new_socket.m_sock = ::accept( m_sock,
          ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );
       if ( new_socket.m_sock <= 0 )
          return false;
       else
          return true;
    }
    
    // Baut die Verbindung zum Server auf
    bool Socket::connect( const string host, const int port ) {
       if ( ! is_valid() )
          return false;      
       struct hostent *host_info;
       unsigned long addr;
       memset( &m_addr, 0, sizeof (m_addr));
       if ((addr = inet_addr( host.c_str() )) != INADDR_NONE) {
           /* argv[1] ist eine numerische IP-Adresse */
           memcpy( (char *)&m_addr.sin_addr,
                   &addr, sizeof(addr));
       }
       else {
           /* Fuer den Fall der Faelle: Wandle den Servernamen  *
            * bspw. "localhost" in eine IP-Adresse um         */
           host_info = gethostbyname( host.c_str() );
           if (NULL == host_info) {
              cout << "Unbekannter Server" << endl;
              exit(1);
           }
           memcpy( (char *)&m_addr.sin_addr, host_info->h_addr,
                    host_info->h_length);
       }
       m_addr.sin_family = AF_INET;
       m_addr.sin_port = htons( port );      
    
       int status = ::connect ( m_sock,
          ( sockaddr * ) &m_addr, sizeof ( m_addr ) );
    
      if ( status == 0 )
        return true;
      else
        return false;
    }
    
    // Daten versenden via TCP
    bool Socket::send( const string s ) const {
       int status = ::send ( m_sock, s.c_str(), s.size(),  0 );
       if ( status == -1 ) {
          return false;
       }
       else {
          return true;
       }
    }
    
    // Daten empfangen via TCP
    int Socket::recv ( string& s ) const {
      char buf [ MAXRECV + 1 ];
      s = "";
      memset ( buf, 0, MAXRECV + 1 );
    
      int status = ::recv ( m_sock, buf, MAXRECV, 0 );
      if ( status > 0 || status != -1 ) {
         s = buf;
         return status;
      }
      else {
         cout << "Fehler in Socket::recv" << endl;
         exit(1);
         return 0;
      } 
    }
    
    // Daten versenden via UDP
    bool Socket::UDP_send( const string addr, const string s, const int port ) const {
       struct sockaddr_in addr_sento;
       struct hostent *h;
       int rc;
    
       h = gethostbyname(addr.c_str());
       if (h == NULL) {
          cout << "Unbekannter Host?" << endl;
          exit(1);
       }
       addr_sento.sin_family = h->h_addrtype;
       memcpy ( (char *) &addr_sento.sin_addr.s_addr,
                h->h_addr_list[0], h->h_length);
       addr_sento.sin_port = htons (port);
       rc = sendto( m_sock, s.c_str(), s.size(), 0,
                     (struct sockaddr *) &addr_sento,
                      sizeof (addr_sento));
       if (rc == -1) {
          cout << "Konnte Daten nicht senden - sendto()" 
               << endl;
          exit(1);
       }        
       return true;
    }
    
    // Daten empfangen vie UDP
    int Socket::UDP_recv( string& s ) const {
       struct sockaddr_in addr_recvfrom;
       int len, n;
       char buf [ MAXRECV + 1 ];
       s = "";
       memset ( buf, 0, MAXRECV + 1 );
       len = sizeof (addr_recvfrom);
       n = recvfrom ( m_sock, buf, MAXRECV, 0,
                      (struct sockaddr *) &addr_recvfrom,
                      ( socklen_t * )&len );
       if (n == -1){
          cout << "Fehler bei recvfrom()" << endl;
          exit(1);
          return 0;
       }
       else {
          s = buf;
          return n;
       } 
    }
    
    // Aus Portabiltaetsgruenden vorhanden
    void Socket::cleanup() const { }
    
    // Socket schliessen
    bool Socket::close() const {
       ::close(m_sock);
       cleanup();
       return true;
    }
    

    echo_client.cpp

    #include "socket.h"
    #include <string>
    #include <iostream>
    using namespace std;
    
    int main (int argc, char *argv[]) {
       if( argc < 2 ){
          cout << "Usage: " << *argv << " ServerAdresse\n";
          exit(1);
       }
       Socket sock;
       sock.create();
       // Adresse des Servers
       string argv_1 = argv[1];
       // Mit dem Server auf Port 15000 verbinden
       sock.connect( argv_1, 15000 );
       cout << "Nachricht an den Server: ";
       string s;
       getline(cin, s, '\n' );
       sock.send( s );
       sock.close();
       return 0;
    }
    

    echo_server.cpp

    // echo_server.cpp
    #include "socket.h"
    #include <string>
    #include <iostream>
    using namespace std;
    
    int main (void) {
       Socket sock1;
       sock1.create();
       // Wir verwenden Port 15000
       sock1.bind(15000);
       sock1.listen();
       while (true) {
          Socket sock2;
          sock1.accept( sock2 ); 
          string s;
          sock2.recv (s);
          cout << "Nachricht von Client erhalten: ";
          cout << s << endl;
          sock2.close();
       }
      sock1.close();
      return 0;
    }
    

    In Code::Blocks krieg ich garkeine Fehlermeldungen sondern nur ein Fenster, dass mich fragt ob ich das Projekt builden will. Wenn ich mit "Ja" antworte kommt das Fenster nochmal, bei "Nein" verschwindet das Fenster und ich bin kein Schritt weiter.
    Wenn ich versuche den Server via gcc zu kompilieren erhalte ich diese Fehlermeldung:

    /tmp/ccj6gNmg.o: In function `__static_initialization_and_destruction_0(int, int)':
    echo_server.cpp:(.text+0x23): undefined reference to `std::ios_base::Init::Init()'
    echo_server.cpp:(.text+0x28): undefined reference to `std::ios_base::Init::~Init()'
    /tmp/ccj6gNmg.o: In function `main':
    echo_server.cpp:(.text+0x62): undefined reference to `Socket::Socket()'
    echo_server.cpp:(.text+0x6b): undefined reference to `Socket::create()'
    echo_server.cpp:(.text+0x79): undefined reference to `Socket::bind(int)'
    echo_server.cpp:(.text+0x82): undefined reference to `Socket::listen() const'
    echo_server.cpp:(.text+0x8b): undefined reference to `Socket::Socket()'
    echo_server.cpp:(.text+0x98): undefined reference to `Socket::accept(Socket&) const'
    echo_server.cpp:(.text+0xa1): undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string()'
    echo_server.cpp:(.text+0xae): undefined reference to `Socket::recv(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&) const'
    echo_server.cpp:(.text+0xb8): undefined reference to `std::cout'
    echo_server.cpp:(.text+0xbd): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
    echo_server.cpp:(.text+0xc9): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
    echo_server.cpp:(.text+0xd1): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
    echo_server.cpp:(.text+0xd6): undefined reference to `std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))'
    echo_server.cpp:(.text+0xdf): undefined reference to `Socket::close() const'
    echo_server.cpp:(.text+0xe8): undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()'
    echo_server.cpp:(.text+0x103): undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()'
    echo_server.cpp:(.text+0x119): undefined reference to `Socket::~Socket()'
    echo_server.cpp:(.text+0x137): undefined reference to `Socket::~Socket()'
    echo_server.cpp:(.text+0x15d): undefined reference to `Socket::~Socket()'
    /tmp/ccj6gNmg.o:(.eh_frame+0x13): undefined reference to `__gxx_personality_v0'
    collect2: ld returned 1 exit status
    

    Ich denke das irgendwas grundlegendes fehlt, aber ich weiß nicht was..
    Hoffe ihr könnt mir helfen.
    Mit freundlichen Grüßen
    Bastelking



  • Kannst du mal den Compiler-Aufruf posten?
    Ein Problem ist aber schonmal, dass du niemals in einer Header-Datei "using namespace" verwenden darfst. Das solltest du ausschließlich in Quell-Dateien (.cpp) verwenden.

    blan



  • Compiler-Aufruf ist:

    gcc echo_server.cpp
    

    Das mit den Namespaces ist wie gesagt direkt aus dem Buch kopiert, aber sobald das Minimalbeispiel läuft, änder ich das.



  • gcc ist für C-Programme, für C++-Programme verwendet man C++. Außerdem musst Du die socket.cpp auch dazukompilieren bzw. -linken.

    In die socket.cpp noch ein #include <cstring> dazu, dann kompiliert das Ding schon mal folgendermaßen: g++ -o server echo_server.cpp socket.cpp

    Und dann bitte noch blans Tip befolgen.



  • Probiers mal mit

    gcc socket.cpp echo_server.cpp
    

    blan



  • Vielen Dank, jetzt funktioniert alles. Zwar noch nicht so richtig in Code::Blocks, aber das bekomme ich auch noch hin. Vielen Dank das ihr mir bei diesem Problem geholfen habt.

    Mit freundlichen Grüßen
    Bastelking



  • nman schrieb:

    gcc ist für C-Programme, für C++-Programme verwendet man C++. Außerdem musst Du die socket.cpp auch dazukompilieren bzw. -linken.

    In die socket.cpp noch ein #include <cstring> dazu, dann kompiliert das Ding schon mal folgendermaßen: g++ -o server echo_server.cpp socket.cpp

    Und dann bitte noch blans Tip befolgen.

    Leider nicht, ich hatte kürzlich mal aus Gewohnheit eine C Datei versehentlich mit der Endung .cpp versehen und durfte mich dann wundern warum er plötzlich an korrektem C etwas zu beanstanden hatte, selbst mit --std=c99 weigerte er sich die Datei anzunehmen. Nachdem ich sie dann in .c umbenannt hatte ging alles wieder einwandfrei.



  • Leider nein schrieb:

    Leider nicht, ich hatte kürzlich mal aus Gewohnheit eine C Datei versehentlich mit der Endung .cpp versehen und durfte mich dann wundern warum er plötzlich an korrektem C etwas zu beanstanden hatte, selbst mit --std=c99 weigerte er sich die Datei anzunehmen. Nachdem ich sie dann in .c umbenannt hatte ging alles wieder einwandfrei.

    Naja, natürlich kannst Du C++-Programme auch direkt mit gcc kompilieren, dann musst Du die Stdlib aber selbst explizit dazulinken.

    Ich weiß, dass gcc nicht automatisch alles für C hält, aber normalerweise gibt es nur wenige Gründe, statt g++ gcc für C++ zu verwenden, darauf wollte ich hinaus.



  • Ich kann dir nur das Buch "Grundkurs Socketprogrammierung mit C unter Linux" vom Vieweg+Teuber - Verlag empfehlen. Damit habe ich mir die Socketprogrammierung angeeignet. Einleitend gibt es dort Informationen über Client, Server, TCP, IP, HTTP und dergleichen. Weiter geht es mit einer kleinen Einführung in die Sprache C. Abgerundet wird jedes Kapitel mit ein paar Aufgaben und kommentierten Lösungswegen. Selbst für C++ - Programmierer wie mich ein hervorragendes Buch.

    So long
    gnunase



  • Die Klassiker zu dem Thema sind die Bücher von Stevens, kann die wärmstens empfehlen. Am besten in der Originalversion.


Anmelden zum Antworten