// socket_text.cc // robert - programme du robot 2005. {{{ // // Copyright (C) 2005 Dufour Jérémy // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Contact : // Web: %WEB% // Email: // }}} #include "socket_text.hh" #include "server_socket.hh" #include "address.hh" #include #include #include #include /// Taille maximum du header static const unsigned max_header_size = 10; /// Constructeur du serveur SocketText::SocketText (ServerSocket &ss) : recvBufPos_ (0), numMessageBuffer_ (0) { headBuf_.resize (max_header_size); socket_ = ss.accept (); } /// Constructeur du serveur avec addresse SocketText::SocketText (ServerSocket &ss, Address &a) : recvBufPos_ (0), numMessageBuffer_ (0) { headBuf_.resize (max_header_size); socket_ = ss.accept (a); } /// Constructeur du client SocketText::SocketText (const Address &a) : recvBufPos_ (0), numMessageBuffer_ (0) { headBuf_.resize (max_header_size); // Création du socket socket_ = socket (AF_INET, SOCK_STREAM, 0); if (socket_ == -1) throw std::runtime_error ("Erreur de création de socket"); // Connexion au serveur if (connect (socket_, a.getSockaddr (), sizeof (sockaddr_in)) == -1) throw std::runtime_error ("Erreur de connexion au serveur"); } /// Envoie d'un buffer de données. SocketText & SocketText::operator<< (const DataBuffer &d) { // Copie du buffer DataBuffer db (d); // Récupération de la taille du message unsigned size = db.size (); // Données du buffer std::vector data (size); // On y place les données dedans db.read ((uint8_t*) &data[0], size); // Encode les données et le places dans le buffer d'envoie encode (&data[0], size, db.type ()); return *this; } /// Reception d'un buffer de données. SocketText & SocketText::operator>> (DataBuffer &d) { if (numMessageBuffer_ > 0) { // Récupération des informations du message qu'on veut retirer unsigned int size = messList_.begin ()->size; d.setType (messList_.begin ()->type); // Récupération du message depuis le buffer std::vector v (size); std::memcpy (&v[0], &recvBuf_[0], size); // Ecriture des données dans le buffer d.write ((uint8_t*) &v[0], v.size ()); // Suppression du message du buffer recvBuf_.erase (recvBuf_.begin (), recvBuf_.begin () + size); messList_.erase (messList_.begin ()); bufferOldSize_ -= size; bufferCurSize_ -= size; recvBufPos_ -= size; numMessageBuffer_--; } return *this; } /// Envoie des données du buffer. int SocketText::send (void) { int status; status = ::send (socket_, &sendBuf_[0], sendBuf_.size (), MSG_NOSIGNAL); if (status == -1 && errno != EWOULDBLOCK) { throw std::runtime_error ("Erreur de send de socket"); } if (status >= 0) { if (status != 0) sendBuf_.erase (sendBuf_.begin (), sendBuf_.begin () + status); return status; } return -1; } /// Encodage du message (rajout du header) void SocketText::encode (const char *data, const int size, const DataBuffer::dataType_e type) { // Création du header (1H4242H) std::ostringstream o; // Rajout du type if (!(o << type)) throw std::runtime_error ("Erreur de convertion int -> string"); o << "H"; if (!(o << size )) throw std::runtime_error ("Erreur de convertion int -> string"); o << "H"; // Recopie du header dans le buffer std::string s = o.str (); std::string::const_iterator sEnd = s.end (); unsigned int compt = 0; std::string::const_iterator i = s.begin (); // Ecriture d'un header de taille fixe while (compt < max_header_size) { if (i != sEnd) { sendBuf_.push_back (*i); i++; } else sendBuf_.push_back ('H'); compt++; } // Rajout des données dans le buffer for (int i = 0; i < size; i++) sendBuf_.push_back (data[i]); } /// Décodage du header. int SocketText::decode (void) { mess_t header; header.type = (DataBuffer::dataType_e) std::atoi (&headBuf_[0]); header.size = std::atoi (&headBuf_[2]); messList_.push_back (header); return header.size; } /// Reception de données dans un vecteur. bool SocketText::recv () { // FIXME C'est KK int status; static unsigned int headerPos = 0; // On n'a pas reçu encore de quoi faire un header if (headerPos < max_header_size) { status = ::recv (socket_, &headBuf_[headerPos], max_header_size - headerPos, MSG_NOSIGNAL); if (status == -1 && errno != EWOULDBLOCK) throw std::runtime_error ("Erreur de send de socket"); if (status > 0) headerPos += status; } // Décodage du header if (headerPos == max_header_size) { bufferOldSize_ = recvBuf_.size (); bufferCurSize_ = bufferOldSize_ + decode (); recvBuf_.resize (bufferCurSize_); headerPos ++; } // Récupération du message lui-même if (headerPos > max_header_size) { status = ::recv (socket_, &recvBuf_[recvBufPos_], bufferCurSize_ - recvBufPos_, MSG_NOSIGNAL); if (status == -1 && errno != EWOULDBLOCK) throw std::runtime_error ("Erreur de send de socket"); if (status > 0) recvBufPos_ += status; if (recvBufPos_ == bufferCurSize_) { headerPos = 0; numMessageBuffer_++; } } return numMessageBuffer_ > 0; } /// Changement du bloquant non/bloquant. void SocketText::nonblock (bool flag) { // Récupération de l'ancien flag. int old = fcntl (socket_, F_GETFL, 0); if (old == -1) throw std::runtime_error ("Erreur de fcntl"); // Change le flag. if (flag) old |= O_NONBLOCK; else old &= ~O_NONBLOCK; // Ecrit le nouveau flag. if (fcntl (socket_, F_SETFL, old) == -1) throw std::runtime_error ("Erreur de fcntl"); }