Sorry to ask this again, but could we please once and for all post some C++ functions that match the Java ones here? They don't seem to be getting added by google, and it is a quite a pain to write them yourself. Below answer using some of this and this.
Here are the two basic versions with boost asio. Notice that to make this work properly, technically, in the second version, one would need to look at how much data is in the buffer, work out how big the header was (VarInt is not a fixed size), but CodedInputStream has GetDirectBufferPointer, with a pointer to where it is, so from this pointer one could work out the remaining message size, compare it with the given message size, construct a new adjusted buffer for the remaining size, and do a asio synchronous read for the rest of the message. Below works as long as messages remain small (I guess around 1 kb or so). If someone has the missing bit, please speak up. Thanks.
writeDelimitedTo in C++:
boost::asio::streambuf request;
{
std::ostream request_stream(&request);
google::protobuf::io::OstreamOutputStream raw_output (&request_stream);
google::protobuf::io::CodedOutputStream coded_output(&raw_output);
coded_output.WriteVarint32(myProtoMsg.ByteSize());
myProtoMsg.SerializeToCodedStream(&coded_output);
}
boost::asio::write(socket,request);
parseDelimitedFrom:
char buf[5000];
void Session::Read()
{
boost::asio::async_read(
socket,
boost::asio::buffer(buf),
boost::asio::transfer_at_least(1),
boost::bind(&Session::Handle_Read,shared_from_this(),boost::asio::placeholders::error));
}
void Session::Handle_Read(const boost::system::error_code& error)
{
if (!error)
{
google::protobuf::io::ArrayInputStream arrayInputStream(buf,5000);
google::protobuf::io::CodedInputStream codedInputStream(&arrayInputStream);
uint32_t messageSize;
codedInputStream.ReadVarint32(&messageSize);
//Read more here
MyProtoMsg myProtoMsg;
myProtoMsg.ParseFromCodedStream(&codedInputStream);
}
Read();
}
EDIT: Above is a bit lazy (with "read more here"). Below is a complete parseDelimitedFrom. Any comments welcome.
NEW parseDelimitedFrom:
static void ReadMyVarint32(int& headerSize,int& messageSize,char buffer[])
{
// Fast path: We have enough bytes left in the buffer to guarantee that
// this read won't cross the end, so we can skip the checks.
char const* ptr = buffer;
char b;
uint32_t result;
b = *(ptr++); result = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= b << 28; if (!(b & 0x80)) goto done;
// If the input is larger than 32 bits, we still need to read it all
// and discard the high-order bits.
for (int i = 0; i < 5; i++) {
b = *(ptr++); if (!(b & 0x80)) goto done;
}
// We have overrun the maximum size of a varint (10 bytes). Assume
// the data is corrupt.
headerSize = 0;
messageSize = 0;
done:
headerSize = ptr - buffer;
messageSize = (int)result;
}
char buf[5000];
int receivedSize(0);
int missingSize(0);
void Session::Read()
{
boost::asio::async_read(
socket,
boost::asio::buffer(buf),
boost::asio::transfer_at_least(1),
boost::bind(&Session::Handle_Read,shared_from_this(),_1,_2));
}
void Session::Handle_Read(const boost::system::error_code& error,std::size_t bytes_transferred)
{
if (!error)
{
int mybytes_transferred((int)bytes_transferred);
if(missingSize == 0)
{
int headerSize, messageSize;
ReadMyVarint32(headerSize,messageSize,buf);
//std::cout << "Read new message: HeaderSize " << headerSize << " MessageSize " << messageSize << " Received: " << mybytes_transferred << std::endl;
for(int i(0);i<mybytes_transferred-headerSize;++i)
request[i] = buf[headerSize+i];
missingSize = headerSize + messageSize - mybytes_transferred;
receivedSize = mybytes_transferred - headerSize;
}
else
{
//std::cout << "Continue message: Read so far " << receivedSize << " Missing " << missingSize << " Received: " << mybytes_transferred << std::endl;
for(int i(0);i<mybytes_transferred;++i)
request[receivedSize+i] = buf[i];
missingSize -= mybytes_transferred;
receivedSize += mybytes_transferred;
}
if(missingSize < 0)
{
//Received too much, give up
missingSize = 0;
receivedSize = 0;
}
else if(missingSize == 0)
{
// Use your proto class here
RequestWrapperPtr requestWrapperPtr(new RequestWrapper());
if(requestWrapperPtr->ParseFromArray(request,receivedSize))
{
HandleRW(requestWrapperPtr);
}
else
{
// std::cout << BaseString() << "Session Handle_Read: Failed to parse!";
}
}
Read();
}
}
I know this question has been answered, but the question is super old now, and updates have been made to the protobuf code repository since then.
Protobuf now has these functions under the header file here:
#include <google/protobuf/util/delimited_message_util.h>
You can check out the associated cpp file on the git repo here.
I have tried out the functions with a boost socket and they work as expected.
Related
I am receiving data following a certain format over a TCP server by serializing them.
the class of the object:
class Command {
private:
char letter;
int x;
int y;
std::string button;
public:
Command(char _letter, int _x, int _y, std::string _button) {
letter = _letter;
x = _x;
y = _y;
button = _button;
}
Command(std::string serializedCmd)
{
size_t delimPos = 0;
std::vector<std::string> parts;
while ((delimPos = serializedCmd.find(SERIALIZE_DELIM)) != std::string::npos)
{
parts.push_back(serializedCmd.substr(0, delimPos));
serializedCmd.erase(0, delimPos + 1);
}
if (parts.empty()) {
this->letter = '$';
this->x = -1;
this->y = -1;
this->button = "nan";
return;
}
this->letter = (char)atoi(parts.at(0).data());
this->x = atoi(parts.at(1).data());
this->y = atoi(parts.at(2).data());
this->button = parts.at(3);
}
Command() {}
~Command() {}
std::string serialize()
{
return std::to_string(letter) + SERIALIZE_DELIM + std::to_string(x) + SERIALIZE_DELIM + std::to_string(y) + SERIALIZE_DELIM + button + SERIALIZE_DELIM;
}
char getLetter() { return letter; }
int getX() { return x; }
int getY() { return y; }
std::string getButton() { return button; }
bool isEmpty() {
return((this->letter == '$') && (this->x == -1) && (this->y == -1) && (this->button == "nan"));
}
void printCommand() {
std::cout << "letter: " << letter << std::endl;
std::cout << "x : " << x << std::endl;
std::cout << "y : " << y << std::endl;
std::cout << "button: " << button << std::endl;
std::cout << "================" << std::endl;
}
};
The data after being DeSerialized at the clients end follows this format:
||{key}|{x}|{y}|{button}||
Example: ||$|20|40|nan||
The problem is that when using recv to get the data, it seems that I'm picking up some noise around the command.
Example:
Sending:
||$|301|386|nan||
Reciving:
(¿ⁿ8T√|301|386|╠╠↕▼
The command is there although it's crowded with noise for some reason.
The code I'm using to receive the data:
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
const int size = recv(sock, buf, sizeof(buf), 0);
std::string s(buf,size);
std::cout << s << std::endl;
buf[size] = 0;
commandTemp = buf;
if (!commandTemp.empty()) {
try {
c = Command(commandTemp);
exe(c); //executes command (unrelated)
}
catch (const std::exception& err) {
std::cout << "Couldn't execute!!!!!!!!" << std::endl;
}
}
else {
std::cout << "Error empty command!\n";
}
}
If I am missing any information I will happily provide it.
Can someone maybe tell what the problem is?
You have to loop on the recv till you get the entire message
This may not be the immediate cause of you problem but you will hit it eventually.
TCP is a stream protocol, not a message protocol. All that TCP guarantees is that the bytes you send are received once and in order. But you might send one 100 byte message and receive 20 5 byte messages. You will say "but it works now", true if on the same machine or the messages are small but not true with larger message over a real netwrok so you must do this
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
int offset = 0;
int len = ??;
while(len > 0){
const int size = recv(sock, buf + offset, sizeof(buf) - offset, 0);
if (size == 0)
break; // record that we got incomplete message
offset += size;
len -= size;
}
....
Note that you need to know the length in advance too. So either send fixed length message or prepend a fixed size length to each message and read that first
I am trying to compress and decompress raw PCM (16-Bit) audio, using OPUS.
Here below is my code for opus_encoder.c. If I remove my decoder.c, the buffer works just fine as in the microphone is able to take in raw PCM data. However, once I have implemented my decoder class, it gave me a lot of errors such as memory allocation, heap corruption and so on. Here are some of my errors:
std::bad_alloc at memory location 0x0031D4BC
Stack overflow (parameters: 0x00000000, 0x05122000)
Access violation reading location 0x04A40000.
Based on my understanding, I think my decoder size cannot allocate the memory properly. Can you take a look at my codes and see what went wrong?
Opus_encoder.c
#include "opusencoder.h"
#include <QtConcurrent/QtConcurrent>
opusencoder::opusencoder(){
}
opusencoder::~opusencoder(){
}
OpusEncoder *enc;
int error;
unsigned char *compressedbuffer;
opus_uint32 enc_final_range;
short pcm = 0;
unsigned char *opusencoder::encodedata(const char *audiodata, const unsigned int& size) {
if (size == 0)
return false;
enc = (OpusEncoder *)malloc(opus_encoder_get_size(1));
enc = opus_encoder_create(8000, 1, OPUS_APPLICATION_VOIP, &error);
if (enc == NULL)
{
exit;
}
opus_int32 rate;
opus_encoder_ctl(enc, OPUS_GET_BANDWIDTH(&rate));
this->encoded_data_size = rate;
int len;
for (int i = 0; i < size / 2; i++)
{
//combine pairs of bytes in the original data into two-byte number
//convert const char to short
pcm= audiodata[2 * i] << 8 | audiodata[(2 * i) + 1];
}
qDebug() << "audiodata: " << pcm << endl;
compressedbuffer = new (unsigned char[this->encoded_data_size]);
len = opus_encode(enc, &pcm, 320, compressedbuffer, this->encoded_data_size);
len = opus_packet_unpad(compressedbuffer, len);
len++;
if (len < 0)
{
qDebug() << "Failure to compress";
return NULL;
}
qDebug() << "COmpressed buffer:" << compressedbuffer << endl;
qDebug() << "opus_encode() ................................ OK.\n" << endl;
}
Opus_decoder.c
##include "opusdecoder.h"
#include <QtConcurrent/QtConcurrent>
#define OPUS_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst))))
int num_channels = 1;
opusdecoder::opusdecoder(){
}
opusdecoder::~opusdecoder(){
}
opus_int16* opusdecoder::decodedata(int frame_size, const unsigned char *data)
{
dec = opus_decoder_create(8000, 1, &err);
if (dec == NULL)
{
exit;
}
opus_int32 rate;
opus_decoder_ctl(dec, OPUS_GET_BANDWIDTH(&rate));
rate = decoded_data_size;
this->num_channels = num_channels;
int decodedatanotwo;
opus_int16 *decompress = new (opus_int16[frame_size * this->num_channels]);
opus_packet_get_nb_channels(data);
decodedatanotwo= opus_decode(dec, data, this->decoded_data_size, decompress, 320, 0);
if (decodedatanotwo < 0)
{
qDebug() << "Failure to decompress";
return NULL;
}
qDebug() << "opus_decode() ................................ OK.\n" << decodedatanotwo << endl;
if (decodedatanotwo != frame_size)
{
exit;
}
}
I have a data buffer which contains multiple compressed members, it could be deflate or zlib compressed member.
I found that zlib inflate call returns Z_STREAM_END after processing the first compressed block, Here multiple compressed member can be in any number(here in my example Its 3). But this data comes from other sides which doesn't communicated detail about number of compressed member in a data.
So how could I implement the use of zlib inflate functionality so that it could work over multiple compressed member ?
Following is a sample quick & dirty example in which I try to elaborate my problem.
This referred the case with zlib 1.2.5 library.
/* example.c -- understanding zlib inflate/decompression operation
*/
#define CHECK_ERR(err, msg) { \
if (err != Z_OK) { \
std::cerr << msg << " error: " << err << std::endl; \
exit(1); \
} \
}
/* ===========================================================================
* deflate() to create compressed data
*/
void test_deflate(std::vector<uint8_t> & input_data, std::vector<uint8_t>& compr)
{
z_stream c_stream; /* compression stream */
int err;
compr.clear();
c_stream.zalloc = (alloc_func)0;
c_stream.zfree = (free_func)0;
c_stream.opaque = (voidpf)0;
err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
CHECK_ERR(err, "deflateInit");
c_stream.next_in = &input_data[0];
c_stream.avail_in = input_data.size();
for (;;) {
uint8_t c_buffer[10] = {};
c_stream.next_out = &c_buffer[0];
c_stream.avail_out = 10;
err = deflate(&c_stream, Z_FINISH);
if (err == Z_STREAM_END)
{
for (int i = 0; i < (10 - c_stream.avail_out); i++)
compr.push_back(c_buffer[i]);
break;
}
CHECK_ERR(err, "deflate");
for (int i = 0; i < (10 - c_stream.avail_out); i++)
compr.push_back(c_buffer[i]);
}
std::cout << "Compressed data (size = " << std::dec << compr.size() << ") = ";
for (int i = 0; i < compr.size(); i++)
std::cout << (uint32_t) compr[i];
std::cout << std::endl;
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate()
*/
void test_inflate(std::vector<uint8_t> &compr,
std::vector<uint8_t> &uncompr)
{
int err;
z_stream d_stream; /* decompression stream */
uncompr.clear();
d_stream.zalloc = Z_NULL;
d_stream.zfree = Z_NULL;
d_stream.opaque = Z_NULL;
d_stream.avail_in = 0;
d_stream.next_in = Z_NULL;
err = inflateInit(&d_stream);
CHECK_ERR(err, "inflateInit");
d_stream.avail_in = compr.size();
d_stream.next_in = &compr[0];
for(;;) {
uint8_t d_buffer[10] = {};
d_stream.next_out = &d_buffer[0];
d_stream.avail_out = 10;
err = inflate(&d_stream, Z_NO_FLUSH);
if (err == Z_STREAM_END) {
for (int i = 0; i < (10 - d_stream.avail_out); i++)
uncompr.push_back(d_buffer[i]);
if (d_stream.avail_in == 0)
break;
}
CHECK_ERR(err, "inflate");
for (int i = 0; i < (10 - d_stream.avail_out); i++)
uncompr.push_back(d_buffer[i]);
}
err = inflateEnd(&d_stream);
CHECK_ERR(err, "inflateEnd");
std::cout << "Uncompressed data (size = " << std::dec << uncompr.size() << ") = ";
for (int i = 0; i < uncompr.size(); i++)
std::cout << (uint32_t) uncompr[i];
std::cout << std::endl;
}
/* ===========================================================================
* Usage: example
*/
int main(int argc, char **argv)
{
std::vector<uint8_t> input_data;
std::vector<uint8_t> compr, multiple_compr;
std::vector<uint8_t> uncompr;
std::cout << "Input Data (in hex) = ";
for (int i=0; i<32; i++) {
input_data.push_back((uint8_t)i);
if( i && (i % 2 == 0))
std::cout << " ";
std::cout << std::hex << (uint32_t)input_data[i];
}
std::cout << std::endl;
// create compressed buffer-1 from input data
test_deflate(input_data, compr);
// copy compressed buffer-1 data into multiple compressed member buffer
multiple_compr = compr;
compr.clear();
// create compressed buffer-2 from input data
test_deflate(input_data, compr);
// append data of compressed buffer-2 into multiple compressed member buffer
for(int i=0; i< compr.size(); i++)
{
multiple_compr.push_back(compr[i]);
}
// create decompressed output
test_inflate(multiple_compr, uncompr);
// compare decompressed data with input data
std::vector<uint8_t> final_data;
final_data.push_back(input_data);
final_data.push_back(input_data);
if (final_data == uncompr)
std::cout << "Matched" << std::endl;
else
std::cout << "Not Matched" << std::endl;
return 0;
}
1) Here second time inflate call returns error, But I wants it proceed successfully why it work like this ?
2) When I use Z_FINISH in the inflate call argument it returns with error, why can't I use Z_FINISH here ?
Kindly correct my example and suggest some optimized approach to do the same.
Simply repeat the inflate operation on the remaining data.
You can save some unnecessary free's and malloc's by using inflateReset() instead of inflateEnd() and inflateInit(). You may have some leftover data from the last inflate in next_in and avail_in, so use that first, and then reload.
I need to read a VarInts from linux sockets in C/C++. Any library, idea or something?
I tried reading and casting char to bool[8] to try without success to read a VarInt...
Also, this is for compatibility with new Minecraft 1.7.2 communication protocol, so, the documentation of the protocol may also help.
Let me explain my project: I'm making a Minecraft server software to run in my VPS (because java is too slow...) and I got stuck with the protocol. One thread waits for the connections and when it has a new connection, it creates a new Client object and starts the Client thread that starts communicating with the client.
I think that there is no need to show code. In case I'm wrong, tell me and I'll edit with some code.
First off, note that varints are sent as actual bytes, not strings of the characters 1 and 0.
For an unsigned varint, I believe the following will decode it for you, assuming you've got the varint data in a buffer pointed to by data. This example function returns the number of bytes decoded in the reference argument 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;
}
To decode a signed varint, you can use this second function that calls the first:
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) );
}
I believe both of these functions are correct. I did some basic testing with the code below to verify a couple datapoints from the Google page. The output is correct.
#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;
}
To encode varints, you could use the following functions. Note that the buffer uint8_t *const data should have room for at least 10 bytes, as the largest varint is 10 bytes long.
#include
// 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 );
}
I am currently trying to integrate a POS system with an Artema Hybrid CC handheld. I am wondering if anyone else has worked on this or something similar.
I can read from the device, that is I receive the ENQ, and send back an ACK in a thread, and I keep it open for reading/writing, but everything I try to write from it simply does nothing.
Here is the code for the function to write the data:
void PayLife::sendPayLifeData(QString data) {
int len = data.length();
int i = 0;
char lrc = 0;
char stx = 0x02;
char etx = 0x03;
char ack = 0x06;
char * bytes;
int ret;
char buffer[132];
bytes = (char *) malloc(sizeof(char) * len + 10);
strcpy(bytes,data.toLatin1().data());
qDebug() << "PayLife Sending data: " << data << " of len " << QString::number(len) <<
" " << " Bytes is: " << bytes ;
while (i < len) {
lrc ^= bytes[i];
i++;
}
/* sprintf(buffer,"%c%c%s%c%c",ack,stx,bytes,etx,lrc);
for (i = 0; i < strlen(buffer); i++) {
printf("c: %X ", buffer[i]);
}
printf(" [[ %s ]] \n", buffer); */
qDebug() << "Starting";
write(this->descriptor,&ack,1);
usleep(100000);
write(this->descriptor,&stx,1);
usleep(100000);
ret = write(this->descriptor,bytes,132);
usleep(100000);
write(this->descriptor,&etx,1);
usleep(100000);
write(this->descriptor,&lrc,1);
qDebug() << "Done";
free(bytes);
}
The data argument is: E11U000008507000099VZ000000
Of course, the documentation is in German, which I don't speak, so this is as far as I have gotten. I've basically got 1 month to implement this then I have to give the device back.
If anyone has any pointers, or some example code that would be awesome.
/jason
The transport protocol looks like something standard, so maybe you should only send len bytes of the data not 132 and include the ETX character in the lrc summing ?
It might be easier and clearer to use QByteArray instead of malloc arrays or QString (QString are for user displayed strings which is not the case here):
void PayLife::sendPayLifeData(const QByteArray & data) {
static const char stx = 0x02;
static const char etx = 0x03;
static const char ack = 0x06;
QByteArray buffer = stx + data + etx;
// Calculate the xor sum on data + etx
char lrc = 0;
for(int i = 1; i < buffer.size(); ++i) {
lrc ^= buffer[i];
}
buffer += lrc;
qDebug() << "Starting";
write(this->descriptor, &ack, 1);
write(this->descriptor, buffer.data(), buffer.size());
qDebug() << "Done";
}
You should also have a look at QSerialDevice, it could allow you to implement the protocol in a more event driven way with signal and slots.