Sockets und Boost-Threads
-
Hallo Zusammen,
ich habe zwar schon einige Jahre Programmiererfahrung (Java, VB, Object Pascal), bin aber was c++ angeht totaler Anfänger. Nicht desto trotz interessiert mich die Sprache sehr und ich wollte mich nun in das Thema Sockets und Threads einarbeiten.
Dazu habe ich mir zwei rudimentäre Klassen geschrieben:
(Teile des Codes stammen aus dem Buch c++ von a bis z)Serversocket.h
#ifndef SERVERSOCKET_H_ #define SERVERSOCKET_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> #include "Socket.h" using namespace std; const int MAXCONNECTIONS = 5; class Serversocket { private: int m_sock; sockaddr_in m_addr; public: Serversocket(int port); virtual ~Serversocket(); bool create(); bool bind( const int port ); bool listen() const; Socket accept (); bool is_valid() const { return m_sock != -1; } }; #endif
Serversocket.cpp
/* * serversocket.cpp * * Created on: Jan 28, 2012 * Author: soahc */ #include "Serversocket.h" Serversocket::Serversocket(int port) { Serversocket::create(); Serversocket::bind(port); Serversocket::listen(); } Serversocket::~Serversocket() { // TODO Auto-generated destructor stub } bool Serversocket::create() { m_sock = ::socket(AF_INET,SOCK_STREAM,0); int y=1; setsockopt( m_sock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int)); return true; } bool Serversocket::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; } bool Serversocket::listen() const { if ( ! is_valid() ) { return false; } int listen_return = ::listen ( m_sock, MAXCONNECTIONS ); if ( listen_return == -1 ) { return false; } return true; } Socket Serversocket::accept () { Socket new_socket; 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 new_socket; else return new_socket; }
Socket.h
#ifndef SOCKET_H_ #define SOCKET_H_ #include <iostream> #include <netinet/in.h> #include <unistd.h> using namespace std; const int MAXRECV = 1024; class Socket { private: sockaddr_in m_addr; public: int x; int m_sock; Socket(); virtual ~Socket(); bool send ( const string ) const; int recv ( string& ) const; bool is_valid() const { return m_sock != -1; } }; class SockExcept { private: string except; public: SockExcept( string s ) : except( s ) {}; ~SockExcept() {}; string get_SockExcept() { return except; } }; #endif
Socket.cpp
#include <string> #include <cstring> #include "Socket.h" using namespace std; Socket::Socket() : m_sock(0) { } Socket::~Socket() { if ( is_valid() ) ::close( m_sock ); } 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; } } 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 { throw SockExcept("Fehler in Socket::recv"); return 0; } }
Der Serversocket macht im prinziep nichts anderes, als einen Socket an einen bestimmten Port zu binden und am ihm zu lauschen. Verbindet sich ein Client mit diesem Port wird die Verbindung zu ihm von der Funktion accept() durch ein Socket-Objekt zurück gegeben.
Ein einfaches ausgeben des vom Client gesendetten Datenstroms konnte ich wie folgt realisieren:
Server MT.cpp
#include <iostream> #include "socket/Serversocket.h" #include "socket/Socket.h" using namespace std; int main() { Serversocket server(15000); cout << "Serversocket erzeugt" << endl; Socket client = server.accept(); cout << "Client akzeptiert" << endl; while(true){ string data; client.recv(data); if ( data.length()>0){ cout << data << endl; } } return 0; }
Das funktioniert soweit auch wunderbar, jedoch prinziepbedingt nur mit einem Client. Nun wollte ich das ganze so erweitern, dass es eben auch mit mehreren Clienten funktioniert. Da ich bei Java mit Threads gute erfahrungen gesammelt habe, wollte ich diese hier auch anwenden. Konkret wollte ich boost-threads verwenden.
Meine angepasste Main-Klasse sieht nun wie follgt aus:
#include <iostream> #include <boost/thread/thread.hpp> #include <boost/thread/xtime.hpp> #include "socket/Serversocket.h" #include "socket/Socket.h" using namespace std; struct MyThreadFunc { Socket client; void operator()() { try { while(true){ string data; client.recv(data); if ( data.length()>0){ cout << data << endl; } } }catch (SockExcept& e) { cout << "Exception wurde ausgeworfen: " << e.get_SockExcept() << endl; } } }; int main() { Serversocket server(15000); cout << "Serversocket erzeugt" << endl; while (true){ struct MyThreadFunc threadFunc; Socket client = server.accept(); threadFunc.client = client; cout << "Client akzeptiert" << endl; boost::thread myThread(threadFunc); boost::thread::yield(); } return 0; }
Leider funktioniert das ganze aber nicht. Die Funktionsaufruf von recv in Socket::send liefert in diesem Fall ein -1 zurück, was zum werfen der SockExcept-Exception führt. Eine abfrage des Errorcodes mit strerror() liefert dann "Unknown error 4294967295". Eine google-Recherche zu dem error-code lässt mich nun vermuten, dass das problem womöglich tiefer liegt. Ich muss dazu sagen, dass ich in einer Virtuellen Maschiene (VMWare Fusion mit Ubuntu 10.04) auf einem Macbook mit MacOS 10.7 entwickle.
Nun zu meiner Konkreten Frage. Geht das überhaupt so, wie ich mir das vorgestellt habe, oder muss ich bei der verwendung der boost-threads noch etwas anderes beachten?
liebe Grüße,
Soahc
-
ich weiß nicht welche gcc version du verwendest aber std::thread ist deutlich einfacher in der Handhabung besonders mit lambda, zu dem musste beachten die posix api in C geschrieben ist, was zur folge das du inbesondere bei threads darauf achten musst das du nicht in nicht Initialisierten speicher schreibst, denn C kann mit Objekten nicht viel anfangen . Dazu kann ich das Tool Valgrind empfehlen.
-
Soahc schrieb:
(Teile des Codes stammen aus dem Buch c++ von a bis z)
Ursache des Problems gefunden!
-
Soahc schrieb:
Hallo Zusammen,
...
Serversocket.h
... using namespace std; ...
Nie "using namespace std" im Header. Ich verwende es auch im cpp nicht, aber da ist es nicht so schädlich.
Kurz zur Erläuterung: Das funktioniert nicht:
#include <iostream> int main() { cout << "Hello World!" << endl; }
Aber das funktioniert:
#include <iostream> #include "Serversocket.h" int main() { cout << "Hello World!" << endl; }
Obwohl Serversocket so rein gar nichts mit cout zu tun hat . Das ist sehr iritierend. Meine Empfehlung ist, alle Bezeichner voll zu qualifizieren:
#include <iostream> int main() { std::cout << "Hello World!" << std::endl; }