I Have some XDR data packets which are sent over a TCP socket. Here a snippet of my code:
const size_t BUFFER_LENGTH = 2000;
XDR xdr;
char *buffer = new char[BUFFER_LENGTH];
xdrmem_create(&xdr, buffer, BUFFER_LENGTH, XDR_ENCODE);
const std::string requestId = request->getPacket()->getRequestId();
MyPacket responcePacket(requestId, status,message);
responcePacket.serialize(&xdr);
unsigned int byteSent = write(*socketDescriptor_, buffer, BUFFER_LENGTH);
delete buffer;
if(byteSent!=BUFFER_LENGTH)
{
throw Exception("Error during write!");
}
where the packet is serialized as follow:
class MyPacket:
void MyPacket::serialize(XDR* xdr)
{
// Convert Enum to int
int _messageType = (int) messageType_;
uint32_t _status = (uint32_t) status_;
//Copy all strings to c_string
char *_requestId = new char[requestId_.length() + 1];
std::strcpy(_requestId,requestId_.c_str());
char *_message = new char[message_.length() + 1];
std::strcpy(_message,message_.c_str());
bool status = xdr_int(xdr, &_messageType) &&
xdr_string(xdr, &_requestId, REQUESTID_LENGTH_) &&
xdr_u_int(xdr, &_status ) &&
xdr_string(xdr, &_message, MESSAGE_LENGTH_);
delete _requestId;
delete _message;
if(!status)
{
throw new Exception("Error while serializing MyPacket.");
}
}
....
}
As a first test I am assuming the maximum packet size to be 2000 and I am always reading 2000 from the other side. As a first test this work fine but I would like to be able to send and receive less information when not required. Furthermore i do not want to have to recompile the client in case i increase the packet size on my server.
I would like to know if there is a proper way to send and receive this stream without having to prepend the packet size myself. And In case I have to prepend this myself is there a way to easily get the xdr size?
Cheers,
ADDITION: I tried using a xdr_rec buffer as follows:
XDR ixdr;
int i;
xdrrec_create (&ixdr,0,0,reinterpret_cast<char*> (&ui),&underflow,0);
ixdr.x_op = XDR_DECODE;
cout << "xdrrec_eof: " << xdrrec_eof (&ixdr) <<endl;
cout << "xdrrec_skiprecord: " << xdrrec_skiprecord (&ixdr) <<endl;
for(j=0;j<10;j++){
xdr_uint32_t (&ixdr, &i);
cerr << "i: " << i << endl;
}xdr_destroy (&ixdr);
If i feed to it a correct buffer with 10 uint32 all works great.
Now I tried to cut some bytes at the end of my buffer and I was expecting either xdrrec_eof
or xdrrec_skiprecord
to give me an error. This is what I wanted to use to detect that I did not have received all the data yet. What happens instead is that both return with success and the xdr_uint32_t
blocks the code execution. So what i now really still miss is a way to detect that i had received the full packet before start decoding it with xdr_uint32_t
. Any suggestion?
The packet size is irrelevant. You just have to read when the XDR library asks you to read. The XDR xdr_rec
stream type deals with streams with length words in them. All you have to do is supply it with your own read and write functions. It's 22 years since I did this but it wasn't difficult. I don't understand why you think you need to 'receive a full packet before start[ing] decoding'. You don't. xdr_rec
will call your read()
function as often as it needs to.
The problem is that xdr_uint32_t is blocking
No. XDR functions are not blocking. Not one of them. Read the source. It is the read()
function that blocks.
so if i have the read and the xdr_uint32_t in the same thread and i run xdr_uint32_t before having finished reading the whole stream
That doesn't make sense. Read what I wrote above. 'You just have to read when the XDR library asks you to read.' Not before.
this risks to block the application. So probably i need to have the socket reading and the decoding in 2 separate threads.
No. You don't seem to understand how xdr_rec
works. You create an xdr_rec
object and supply it with a read callback function and a write callback function. From there on you just call whatever XDR routines you like, and they call the read or write callbacks as appropriate. You don't call them at all. There is no requirement to 'read the whole stream' first.
You should do all this in a single thread.