【问题标题】:Read "varint" from linux sockets从 linux 套接字读取“varint”
【发布时间】:2013-11-14 12:19:45
【问题描述】:

我需要从 C/C++ 中的 linux 套接字读取 VarInts。任何图书馆,想法或什么?

我尝试读取并将 char 转换为 bool[8] 以尝试读取 VarInt 没有成功...

另外,这是为了与新的 Minecraft 1.7.2 通信协议兼容,因此,the documentation of the protocol 也可能有所帮助。

让我解释一下我的项目:我正在制作一个 Minecraft 服务器软件以在我的 VPS 中运行(因为 java 太慢了......),但我被协议卡住了。一个线程等待连接,当它有新连接时,它会创建一个新的客户端对象并启动客户端线程,该线程开始与客户端通信。

我认为没有必要展示代码。如果我错了,请告诉我,我会用一些代码进行编辑。

【问题讨论】:

  • 我以前从未见过 varint,但我尝试在 Google 上搜索“varint c library”并获得了一些成功。
  • 又一篇“Java 很慢”的帖子。

标签: c++ c linux sockets


【解决方案1】:

首先,请注意 varints 作为实际字节发送,而不是字符串 10

对于一个无符号的 varint,我相信下面的代码会为你解码,假设你已经在 data 指向的缓冲区中获得了 varint 数据。此示例函数返回在引用参数int decoded_bytes 中解码的字节数。

uint64_t decode_unsigned_varint( const uint8_t *const data, int &decoded_bytes )
{
    int i = 0;
    uint64_t decoded_value = 0;
    int shift_amount = 0;

    do 
    {
        decoded_value |= (uint64_t)(data[i] & 0x7F) << shift_amount;     
        shift_amount += 7;
    } while ( (data[i++] & 0x80) != 0 );

    decoded_bytes = i;
    return decoded_value;
}

要解码已签名的 varint,您可以使用调用第一个函数的第二个函数:

int64_t decode_signed_varint( const uint8_t *const data, int &decoded_bytes )
{
    uint64_t unsigned_value = decode_unsigned_varint(data, decoded_bytes);
    return (int64_t)( unsigned_value & 1 ? ~(unsigned_value >> 1) 
                                         :  (unsigned_value >> 1) );
}

我相信这两个函数都是正确的。我使用下面的代码进行了一些基本测试,以验证来自 Google 页面的几个数据点。输出是正确的。

#include <stdint.h>
#include <iostream>


uint64_t decode_unsigned_varint( const uint8_t *const data, int &decoded_bytes )
{
    int i = 0;
    uint64_t decoded_value = 0;
    int shift_amount = 0;

    do 
    {
        decoded_value |= (uint64_t)(data[i] & 0x7F) << shift_amount;     
        shift_amount += 7;
    } while ( (data[i++] & 0x80) != 0 );

    decoded_bytes = i;
    return decoded_value;
}

int64_t decode_signed_varint( const uint8_t *const data, int &decoded_bytes )
{
    uint64_t unsigned_value = decode_unsigned_varint(data, decoded_bytes);
    return (int64_t)( unsigned_value & 1 ? ~(unsigned_value >> 1) 
                                         :  (unsigned_value >> 1) );
}



uint8_t ex_p300[] = { 0xAC, 0x02 };
uint8_t ex_n1  [] = { 0x01 };

using namespace std;

int main()
{
    int decoded_bytes_p300;
    uint64_t p300;

    p300 = decode_unsigned_varint( ex_p300, decoded_bytes_p300 );

    int decoded_bytes_n1;
    int64_t  n1;

    n1 = decode_signed_varint( ex_n1, decoded_bytes_n1 );

    cout << "p300 = " << p300 
         << "   decoded_bytes_p300 = " << decoded_bytes_p300 << endl;

    cout << "n1 = " << n1 
         << "   decoded_bytes_n1 = " << decoded_bytes_n1 << endl;

    return 0;
}

要对变量进行编码,您可以使用以下函数。请注意,缓冲区uint8_t *const data 应至少有 10 个字节的空间,因为最大的 varint 长度为 10 个字节。
#包括

// Encode an unsigned 64-bit varint.  Returns number of encoded bytes.
// 'buffer' must have room for up to 10 bytes.
int encode_unsigned_varint(uint8_t *const buffer, uint64_t value)
{
    int encoded = 0;

    do
    {
        uint8_t next_byte = value & 0x7F;
        value >>= 7;

        if (value)
            next_byte |= 0x80;

        buffer[encoded++] = next_byte;

    } while (value);


    return encoded;
}

// Encode a signed 64-bit varint.  Works by first zig-zag transforming
// signed value into an unsigned value, and then reusing the unsigned
// encoder.  'buffer' must have room for up to 10 bytes.
int encode_signed_varint(uint8_t *const buffer, int64_t value)
{
    uint64_t uvalue;

    uvalue = uint64_t( value < 0 ? ~(value << 1) : (value << 1) );

    return encode_unsigned_varint( buffer, uvalue );
}

【讨论】:

  • 感谢您的回答。也许我跑题了,但是,我该如何编码呢?我想我应该看看位运算符...
  • 编码类似于解码。我将创建一对编码函数并将它们扔到那里。
  • 再次感谢!我不需要保留 10 个字节,因为我正在调整函数以在构造时发送 varint。该函数将从循环中的每一步向客户端发送 1 个字节。
猜你喜欢
  • 2016-04-22
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
  • 2013-10-12
  • 1970-01-01
  • 2012-03-08
相关资源
最近更新 更多