Question

Je suis en train de créer un client de ligne de commande pour minecraft. Il y a une spécification complète sur le protocole qui peut être trouvé ici: http://mc.kev009.com/Protocol. Pour répondre à votre question à l'avance, oui, je suis un peu d'un C ++ Noob.

J'ai diverses questions dans l'application de ce protocole, dont chaque critique.

  1. Le protocole indique que tous les types sont big-endian. Je ne sais pas comment je dois vérifier si mes données sont peu endian et si oui comment convertir au big-endian.
  2. Le type de données chaîne est un peu bizarre un. Il est une chaîne UTF-8 modifié qui est précédé d'une courte contenant la longueur de chaîne. Je ne sais pas comment je devrais emballer cela dans un simple tableau char [], ni comment convertir mes cordes simples en UTF-8 ceux modifiés.
  3. Même si je savais comment convertir mes données big-endian et créer modifiés chaînes UTF-8 je ne sais toujours pas comment emballer les meubles dans un tableau char [] et envoyer cela comme un paquet. Tout ce que je l'ai fait avant est simple réseau HTTP qui est ASCII.

Explications, liens, noms de fonctions connexes et de courts extraits appréciés!

EDIT

1 et 3 réponse maintenant. 1 est répondu ci-dessous par user470379. 3 est répondu par ce fil IMPRESSIONNANT qui explique ce que je veux faire très bien: http://cboard.cprogramming.com/networking-device-communication/68196-sending-non-char*-data.html Je ne suis pas sûr de la UTF- modifiée 8 encore bien.

Était-ce utile?

La solution

Une approche traditionnelle consiste à définir une structure de message C pour chaque message de protocole et mettre en œuvre des fonctions de sérialisation et désérialisation pour elle. Par exemple Login peut être représentée comme ceci:

#include <string>
#include <stdint.h>

struct LoginRequest
{
    int32_t protocol_version;
    std::string username;
    std::string password;
    int64_t map_seed;
    int8_t dimension;
};

fonctions de sérialisation sont obligatoires. D'abord, il a besoin de fonctions de sérialisation pour les entiers et les chaînes, puisque ce sont les types de membres LoginRequest.

Integer fonctions de sérialisation doivent faire des conversions en provenance et à la représentation des big-endian. Étant donné que les membres du message sont copiés et du tampon, l'inversion de l'ordre des octets peut être effectué lors de la copie:

#include <boost/detail/endian.hpp>
#include <algorithm>

#ifdef BOOST_LITTLE_ENDIAN

    inline void xcopy(void* dst, void const* src, size_t n)
    {
        char const* csrc = static_cast<char const*>(src);
        std::reverse_copy(csrc, csrc + n, static_cast<char*>(dst));
    }

#elif defined(BOOST_BIG_ENDIAN)

    inline void xcopy(void* dst, void const* src, size_t n)
    {
        char const* csrc = static_cast<char const*>(src);
        std::copy(csrc, csrc + n, static_cast<char*>(dst));
    }

#endif

// serialize an integer in big-endian format
// returns one past the last written byte, or >buf_end if would overflow
template<class T>
typename boost::enable_if<boost::is_integral<T>, char*>::type serialize(T val, char* buf_beg, char* buf_end)
{
    char* p = buf_beg + sizeof(T);
    if(p <= buf_end)
        xcopy(buf_beg, &val, sizeof(T));
    return p;
}

// deserialize an integer from big-endian format
// returns one past the last written byte, or >buf_end if would underflow (incomplete message)
template<class T>
typename boost::enable_if<boost::is_integral<T>, char const*>::type deserialize(T& val, char const* buf_beg, char const* buf_end)
{
    char const* p = buf_beg + sizeof(T);
    if(p <= buf_end)
        xcopy(&val, buf_beg, sizeof(T));
    return p;
}

Et pour les chaînes (manipulation modifiée UTF-8 de la même façon que les chaînes de ASCIIZ ):

// serialize a UTF-8 string
// returns one past the last written byte, or >buf_end if would overflow
char* serialize(std::string const& val, char* buf_beg, char* buf_end)
{
    int16_t len = val.size();
    buf_beg = serialize(len, buf_beg, buf_end);
    char* p = buf_beg + len;
    if(p <= buf_end)
        memcpy(buf_beg, val.data(), len);
    return p;
}

// deserialize a UTF-8 string
// returns one past the last written byte, or >buf_end if would underflow (incomplete message)
char const* deserialize(std::string& val, char const* buf_beg, char const* buf_end)
{
    int16_t len;
    buf_beg = deserialize(len, buf_beg, buf_end);
    if(buf_beg > buf_end)
        return buf_beg; // incomplete message
    char const* p = buf_beg + len;
    if(p <= buf_end)
        val.assign(buf_beg, p);
    return p;
}

Et un couple de foncteurs d'aide:

struct Serializer
{
    template<class T>
    char* operator()(T const& val, char* buf_beg, char* buf_end)
    {
        return serialize(val, buf_beg, buf_end);
    }
};

struct Deserializer
{
    template<class T>
    char const* operator()(T& val, char const* buf_beg, char const* buf_end)
    {
        return deserialize(val, buf_beg, buf_end);
    }
};

Maintenant, en utilisant ces fonctions primitives que nous pouvons facilement sérialisation et la désérialisation message LoginRequest:

template<class Iterator, class Functor>
Iterator do_io(LoginRequest& msg, Iterator buf_beg, Iterator buf_end, Functor f)
{
    buf_beg = f(msg.protocol_version, buf_beg, buf_end);
    buf_beg = f(msg.username, buf_beg, buf_end);
    buf_beg = f(msg.password, buf_beg, buf_end);
    buf_beg = f(msg.map_seed, buf_beg, buf_end);
    buf_beg = f(msg.dimension, buf_beg, buf_end);
    return buf_beg;
}

char* serialize(LoginRequest const& msg, char* buf_beg, char* buf_end)
{
    return do_io(const_cast<LoginRequest&>(msg), buf_beg, buf_end, Serializer());
}

char const* deserialize(LoginRequest& msg, char const* buf_beg, char const* buf_end)
{
    return do_io(msg, buf_beg, buf_end, Deserializer());
}

En utilisant les functors auxiliaires ci-dessus et représentant des tampons d'entrée / sortie en tant que char itérateur varie seulement un modèle de fonction est nécessaire de faire les deux sérialisation et la désérialisation du message.

Et mettre tous ensemble, l'utilisation:

int main()
{
    char buf[0x100];
    char* buf_beg = buf;
    char* buf_end = buf + sizeof buf;

    LoginRequest msg;

    char* msg_end_1 = serialize(msg, buf, buf_end);
    if(msg_end_1 > buf_end)
        ; // more buffer space required to serialize the message

    char const* msg_end_2 = deserialize(msg, buf_beg, buf_end);
    if(msg_end_2 > buf_end)
        ; // incomplete message, more data required
}

Autres conseils

Pour # 1, vous devrez utiliser ntohs et les amis. Utilisez les *s versions (courtes) pour les entiers de 16 bits et les versions *l (longues) pour les entiers 32 bits. Le hton* (hôte réseau) vous permet de convertir les données sortantes à grand-endian indépendamment du boutisme de la plate-forme que vous êtes, et ntoh* (réseau hôte) se reconvertir de données entrantes (encore une fois, indépendamment de la plate-forme boutisme)

du haut de ma tête ...

const char* s;  // the string you want to send
short len = strlen(s);

// allocate a buffer with enough room for the length info and the string
char* xfer = new char[ len + sizeof(short) ];

// copy the length info into the start of the buffer
// note:  you need to hanle endian-ness of the short here.
memcpy(xfer, &len, sizeof(short));

// copy the string into the buffer
strncpy(xfer + sizeof(short), s, len);

// now xfer is the string you want to send across the wire.
// it starts with a short to identify its length.
// it is NOT null-terminated.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top