Soyez le premier à donner votre avis sur cette source.
Vue 6 351 fois - Téléchargée 207 fois
#ifndef sock_h #define sock_h #include <boost/utility.hpp> #include <string> #ifdef WIN32 #include <winsock.h> #define wsastart(); {WSADATA WsaData; WSAStartup(MAKEWORD(1,1), &WsaData);} #define wsaclean(); WSACleanup(); #pragma comment(lib, "ws2_32.lib") #else #define wsastart(); #define wsaclean(); #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <sys/types.h> #define SOCKET_ERROR (-1) /* défini dans winsock.h */ typedef int SOCKET; #define INVALID_SOCKET (SOCKET)(~0) /* défini dans winsock.h */ #define closesocket close #endif struct socket_error : std::exception {}; struct base_sock : boost::noncopyable { struct address { protected: std::string _name; unsigned short _port; public: address(const address & other) : _name(other._name), _port(other._port) { } address(const std::string & __name, const unsigned short & __port) : _name(__name), _port(__port) { } address(const std::string & __name) : _name(__name), _port(0) { } address(const unsigned short & __port) : _port(__port) { } address() : _port(0) { } address & operator = (const address & other) { _name = other._name; _port = other._port; return *this; } void clear() { _name = ""; _port = 0; } bool is_empty() const { return _name == "" && _port == 0; } const unsigned short & port() const { return _port; } const std::string & name() const { return _name; } unsigned short & set_port(const unsigned short & port) { return _port = port; } std::string & set_name(const std::string & name) { return _name = name; } bool operator == (const address & other) const { return (_name == other._name) && (_port == other._port); } }; protected: address sock_address; SOCKET s; private: void create() { close(); s = socket(AF_INET, SOCK_STREAM, 0); if(s == INVALID_SOCKET) throw socket_error(); } public: base_sock() : s(INVALID_SOCKET) { wsastart(); } virtual ~base_sock() { close(); wsaclean(); } bool good() const { return s != INVALID_SOCKET; } virtual void write(const std::string & data) const { if(!good()) throw socket_error(); if(::send(s, data.data(), (int)data.size(), 0) == SOCKET_ERROR) throw socket_error(); } virtual std::string read() const { if(!good()) throw socket_error(); // on crée un buf avec la taille max de data qu'il peut recevoir unsigned long cbdata; int cbopt = sizeof(cbdata); if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&(cbdata), &cbopt) == SOCKET_ERROR) throw socket_error(); char* data = new char[cbdata]; // on recoit int len; if((len = recv(s, data, cbdata, 0)) == SOCKET_ERROR) { delete[] data; throw socket_error(); } else { std::string ret(data, len); delete[] data; return ret; } } void close() { if(!good()) return; ::closesocket(s); sock_address.clear(); s = INVALID_SOCKET; } void listen(const address & addr) { create(); sockaddr_in sain; sain.sin_family = AF_INET; sain.sin_port = ::htons(addr.port()); if(addr.name() == "") sain.sin_addr.s_addr = INADDR_ANY; else { hostent* host = ::gethostbyname(addr.name().c_str()); if(host == NULL) throw socket_error(); memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4); } if(::bind(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error(); sock_address = addr; if(::listen(s, SOMAXCONN) == SOCKET_ERROR) throw socket_error(); } void accept(const base_sock & listen_sock) { if(!listen_sock.good()) throw socket_error(); create(); sockaddr_in addr; int len = sizeof(sockaddr_in); s = ::accept(listen_sock.s, (sockaddr*)&addr, &len); if(s == INVALID_SOCKET) throw socket_error(); sock_address.set_port(addr.sin_port); sock_address.set_name(inet_ntoa(addr.sin_addr)); } void connect(const address & addr) { create(); sockaddr_in sain; int len = sizeof(sockaddr_in); sain.sin_family = AF_INET; sain.sin_port = ::htons(addr.port()); hostent* host = ::gethostbyname(addr.name().c_str()); if(host == NULL) throw socket_error(); memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4); if(::connect(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error(); sock_address = addr; } const address & get_address() { return sock_address; } class sock_set : boost::noncopyable { // initialisation d'une instance nulle sock_set(const bool & is_null) : bnull(is_null) { } protected: fd_set fds; bool bnull; public: // renvoit un sock_set nul static sock_set & null() { static sock_set ret(true); return ret; } sock_set() : bnull(false) { FD_ZERO(&fds); } inline void add(const base_sock & _s) { if(bnull) throw socket_error(); if(!_s.good()) throw socket_error(); FD_SET(_s.s, &fds); } inline void del(const base_sock & _s) { if(bnull) throw socket_error(); if(!_s.good()) throw socket_error(); FD_CLR(_s.s, &fds); } inline bool is_set(const base_sock & _s) const { if(bnull) throw socket_error(); if(!_s.good()) throw socket_error(); return FD_ISSET(_s.s, &fds) != 0; } static int select(sock_set & read_set, sock_set & write_set, sock_set & error_set, const timeval & timeout) { if(read_set.bnull && write_set.bnull && error_set.bnull) throw socket_error(); fd_set * r = read_set.bnull ? NULL : &(read_set.fds); fd_set * w = write_set.bnull ? NULL : &(write_set.fds); fd_set * e = error_set.bnull ? NULL : &(error_set.fds); int ret = ::select(0, r, w, e, &timeout); if(ret == SOCKET_ERROR) throw socket_error(); return ret; } }; }; #endif
18 janv. 2004 à 01:54
J'attendais avec impatience le résultat de ton travail.
Alors voici quelques (petites) remarques pour commencer. Si l'utilisateur manipules plusieurs instances de base_sock, il execute plusieurs fois WSAStart et WSAClean...aucune idée de l'impact mais un vieux flag static pourrait permettre de l'éviter.
D'autre part, si l'utilisateur souhaite exploiter base_sock pour transmettre de grandes quantitiés d'informations, alors il est nécessaire d'implementer un mécanisme de fragmentation dans write() (contenant l'appel de send()). En effet, la taille du buffer à envoyer ne peut pas exceder SO_MAX_MSG_SIZE. Cela doit etre transparent pour l'utilisateur.
A suivre...
Cordialement,
Xter.
18 janv. 2004 à 14:29
lorsqu'un processus appelle une fois WSAStartup, les dll réseau sont chargées. puis, lorsqu'on le rappelle une 2eme fois, un compteur interne estr sulement incrémenté. pour les appels a WSACleanup, le compteur interne est décrémenté et lorsqu'il atteint 0 les dll sont déchargées.
par contre tu as peut-etre raison dans le sens ou un appel de WSAStartup va négocier la version entre celle des dll reseau et celle que ton prog demande, et ce n'est pas très optimisé de refaire cette négociation à chaque instanciation.
ensuite, pour les grandes quantités d'informations, il faut bien comprendre que ceci n'est pour l'instant qu'une encapsulation 'basique' de l'api c socket. Je suis en train de développer d'autres classes de dérivant de celle-ci pour supporter un transfert crypté, la fragmentation de paquets comme tu en parle, et faire respecter l'interface istream / ostream standard c++ pour la lecture/ecriture d'un socket.
en tout cas merci de ces remarques :)
18 janv. 2004 à 14:55
On constate trop souvent "des fuites" dans nos abstractions et cela est dommagable. En effet, l'utilisateur de l'abstraction base_sock n'est pas censée connaitre les limitations des fonctions de la librairie socket que tu encapsules (en particulier la fonction send).
D'ailleurs, la couche logicielle plus avancée (que tu programmes) et qui exploite base_sock n'est pas non plus censée connaitre ses limitations. Imagine que tu developpes cette couche logicielle avancée à partir d'une absctration base_sock que tu n'aurais pas programmé....
Donc, pour être une parfaite abstraction, base_sock devrait implementer ce mécanisme de fragmentation.
Bon courage pour la suite !
Cordialement,
Xter.
18 janv. 2004 à 15:53
Il faut distinguer les cas >0 et =0. En effet, =0 signifie que la connexion a été fermée.
Cordialement,
Xter.
18 janv. 2004 à 18:10
merci bcp :)
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.