Envoi de message sur TCP / IP à l’aide de sockets

J’essaie d’envoyer des données entre un client / serveur, les données ressemblent à

typedef Struct Message { int id; int message_length; char* message_str; }message; 

J’essaie d’ Write et de Read ce message entre un client et un serveur mettant à jour en permanence les éléments de cette structure. J’ai entendu que Writev pouvait faire l’affaire. Je veux envoyer un message au serveur, puis le serveur extrait les éléments et utilise ces éléments en tant que conditions pour exécuter la méthode appropriée.

En supposant que vous souhaitiez effectuer la sérialisation vous-même et que vous n’utilisiez pas les tampons de protocole Google ou une bibliothèque pour la gérer à votre place, je suggérerais d’écrire une paire de fonctions comme celle-ci:

 // Serializes (msg) into a flat array of bytes, and returns the number of bytes written // Note that (outBuf) must be big enough to hold any Message you might have, or there will // be a buffer overrun! Modifying this function to check for that problem and // error out instead is left as an exercise for the reader. int SerializeMessage(const struct Message & msg, char * outBuf) { char * outPtr = outBuf; int32_t sendID = htonl(msg.id); // htonl will make sure it gets sent in big-endian form memcpy(outPtr, &sendID, sizeof(sendID)); outPtr += sizeof(sendID); int32_t sendLen = htonl(msg.message_length); memcpy(outPtr, &sendLen, sizeof(sendLen)); outPtr += sizeof(sendLen); memcpy(outPtr, msg.message_str, msg.message_length); // I'm assuming message_length=strlen(message_str)+1 here outPtr += msg.message_length; return (outPtr-outBuf); } // Deserializes a flat array of bytes back into a Message object. Returns 0 on success, or -1 on failure. int DeserializeMessage(const char * inBuf, int numBytes, struct Message & msg) { const char * inPtr = inBuf; if (numBytes < sizeof(int32_t)) return -1; // buffer was too short! int32_t recvID = ntohl(*((int32_t *)inPtr)); inPtr += sizeof(int32_t); numBytes -= sizeof(int32_t); msg.id = recvID; if (numBytes < sizeof(int32_t)) return -1; // buffer was too short! int32_t recvLen = ntohl(*((int32_t *)inPtr)); inPtr += sizeof(int32_t); numBytes -= sizeof(int32_t); msg.message_length = recvLen; if (msg.message_length > 1024) return -1; /* Sanity check, just in case something got munged we don't want to allocate a giant array */ msg.message_str = new char[msg.message_length]; memcpy(msg.message_str, inPtr, numBytes); return 0; } 

Avec ces fonctions, vous pouvez maintenant convertir un message en tableau de caractères simple et inversement. Il ne vous rest donc plus qu’à envoyer le tableau de caractères par la connexion TCP, à le recevoir à l’extrémité distante, puis à désérialiser le tableau dans une structure Message.

L’un des problèmes est que vos tableaux de caractères auront une longueur variable (en raison de la présence d’une chaîne pouvant avoir des longueurs différentes). Votre récepteur aura donc besoin d’un moyen simple de savoir combien d’octets à recevoir avant d’appeler DeserializeMessage () le le tableau.

Un moyen facile de gérer cela consiste à toujours envoyer d’abord un entier de 4 octets, avant d’envoyer le tableau de caractères. Le nombre entier de 4 octets doit toujours être la taille du tableau à venir, en octets. (Assurez-vous de convertir d’abord l’entier en big-endian, via htonl (), avant de l’envoyer et de le reconvertir en natif-endian sur le récepteur via htonl () avant de l’utiliser).

D’accord, je vais tenter un coup. Je vais supposer que vous avez un object “message” du côté de l’envoi et que vous voulez le faire, mais l’envoyer d’une manière ou d’une autre à un autre ordinateur et reconstruire les données afin que vous puissiez effectuer des calculs. Vous ne savez peut-être pas comment coder les données pour les communications, puis les décoder du côté de la réception pour récupérer les informations. L’approche simpliste consistant à simplement écrire les octets contenus dans un object “message” (c’est-à-dire écrire (fd, msg, sizeof (* msg), où “msg” est un pointeur sur un object de type “message”) ne fonctionnera pas car vous finirez par envoyer la valeur d’une adresse virtuelle dans la mémoire d’une machine à une machine différente et vous ne pouvez pas faire grand chose avec cela du côté destinataire. Le problème est donc de concevoir un moyen de faire passer deux entiers et un caractère. Enchaînez-les de manière à pouvoir les récupérer à l’autre bout. Il existe bien sûr de nombreuses façons de le faire. Cela décrit-il ce que vous essayez de faire?

Vous pouvez envoyer des structures par socket, mais vous devez les sérialiser avant de l’envoyer à l’aide de la sérialisation boost.

Voici un exemple de code:

  #include #include #include #include  #include  #include  using namespace std; typedef struct { public: int id; int message_length; ssortingng message_str; private: friend class boost::serialization::access; template  void serialize(Archive &ar, const unsigned int vern) { ar & id; ar & message_length; ar & message_str; } } Message; int main() { Message newMsg; newMsg.id = 7; newMsg.message_length = 14; newMsg.message_str="Hi ya Whats up"; std::ssortingngstream strData; boost::archive::text_oarchive oa(strData); oa << newMsg; char *serObj = (char*) strData.str().c_str(); cout << "Serialized Data ::: " << serObj << "Len ::: " << strlen(serObj) << "\n"; /* Send serObj thru Sockets */ /* recv serObj from socket & deserialize it */ std::stringstream rcvdObj(serObj); Message deserObj; boost::archive::text_iarchive ia(rcvdObj); ia >> deserObj; cout<<"id ::: "< 

vous pouvez comstackr le programme en

g ++ -o serial boost.cpp /usr/local/lib/libboost_serialization.a

Vous devez avoir libboost_serialization.a compilé de manière statique dans votre ordinateur.

Garder le blocage des sockets sera une bonne chose et vous devez concevoir pour lire ces structures à partir du tampon recv.