打包UDP网络的消息

时间:2016-06-09 13:20:44

标签: c++ udp

我刚刚在我正在进行的游戏引擎中遇到了以下方法:

bool addmsg(int type, const char *fmt, ...) {
    if (!connected) return false;
    static uchar buf[MAXTRANS];
    ucharbuf p(buf, sizeof (buf));
    putint(p, type);
    int numi = 1, numf = 0, nums = 0, mcn = -1;
    bool reliable = false;
    if (fmt)
    {
        va_list args;
        va_start(args, fmt);
        while (*fmt) switch (*fmt++)
            {
            case 'r': reliable = true; break;
            case 'c':
                {
                    gameent *d = va_arg(args, gameent *);
                    mcn = !d || d == player1 ? -1 : d->clientnum;
                    break;
                }
            case 'v':
                {
                    int n = va_arg(args, int);
                    int *v = va_arg(args, int *);
                    loopi(n) putint(p, v[i]);
                    numi += n;
                    break;
                }

            case 'i':
                {
                    int n = isdigit(*fmt) ? *fmt++ - '0' : 1;
                    loopi(n) putint(p, va_arg(args, int));
                    numi += n;
                    break;
                }
            case 'f':
                {
                    int n = isdigit(*fmt) ? *fmt++ - '0' : 1;
                    loopi(n) putfloat(p, (float)va_arg(args, double));
                    numf += n;
                    break;
                }
            case 's': sendstring(va_arg(args, const char *), p); nums++; break;
            }
        va_end(args);
    }
    int num = nums || numf ? 0 : numi, msgsize = server::msgsizelookup(type);
    if (msgsize && num != msgsize) { fatal("inconsistent msg size for %d (%d != %d)", type, num, msgsize); }
    if (reliable) messagereliable = true;
    if (mcn != messagecn)
    {
        static uchar mbuf[16];
        ucharbuf m(mbuf, sizeof (mbuf));
        putint(m, N_FROMAI);
        putint(m, mcn);
        messages.put(mbuf, m.length());
        messagecn = mcn;
    }
    messages.put(buf, p.length());

    return true;
}

这很可怕。人们实际上认为通过网络发送消息是个好主意:

addmsg(MY_ACTION, "rii3ii5", 1, 42, 42, 42, e.type, e.attr1, e.attr2, e.attr3, e.attr4, e.attr5)

哦,是的!超级可读。现在,我想要积极并慢慢地重构这个。我正在考虑使用模板动态解释消息中的类型并正确编码。有没有人建议如何开始这个废话?谢谢!

2 个答案:

答案 0 :(得分:1)

有Qt方式,有数据流。

class DataStream {
    /* has an internal buffer, can use std::string, std::vector<char> or even uchar [] */

    DataStream &operator << (int i) {
        putint(internal_buffer, i);
        return this;
    }
    /* ... */

    /* Multiple serialization */
    void pack() {
        return;
    }

    template <typename T, typename ...Params>
    void pack(const T &item, Params&&...params) {
        (*this) << item;
        pack(std::forward<Params>(params)...);
    }

    const char* buffer() const {
        return internal_buffer.data();
    }
};

那样,你有:

template<typename ...Params>
bool addmsg(int type, Params &&... params) { 
    DataStream stream;
    stream << type;

    ...

    stream.pack(std::forward<Params>(params)...);

    //Get stream buffer and use that in msg
}

这种事情很危险,你必须确保两个平台上的int都是一样的。使用类型为uint32_t而不是int(在调用addmsg函数时)可能更明智,除非您将DataStream限制为仅一种类型的整数。

您可以扩展DataStream以支持其他类型,例如自定义结构:

struct MyStruct {
    int a,b,c;
    float f;
};

DataStream &operator << (DataStream &stream, const MyStruct &s) {
    stream << s.a << s.b << s.c << s.f;
    return stream;
}

e成员而言,e成员可以直接传递给addmsg函数,而不是5个参数:

addmsg(MY_ACTION, 1, 42, 42, 42, e);

答案 1 :(得分:1)

您可以使用函数重载。不需要模板。

#include <string>

constexpr size_t MAXTRANS = 128;

enum reliable_t { reliable };

class message
{
public:
    void send() { }

    message& operator<<(reliable_t)
    {
        m_reliable = true;
        return *this;
    }

    message& operator<<(int x)
    {
        // add int to message
        return *this;
    }

    message& operator<<(float x)
    {
        // add float to message
        return *this;
    }

    message& operator<<(const std::string& x)
    {
        // add string to message
        return *this;
    }

private:
    unsigned char m_buf[MAXTRANS];
    bool m_reliable = false;
};

int main()
{
    message msg;
    msg << reliable << 42 << 42 << "ultimate question";
    msg.send();
    return 0;
}

以下是coliru示例的链接。