Packeting and Tokenizing TCP/IP
Ok so I am redoing my message handling on my server and client, however I am confused on best way to implament this. I have read through a dozen tutorial and faq's and here is my current ideas, and where I see them fail, as well as an idea on how to proceed. Please give any suggestions (or flames) on how it wont or will work. (1) I was thinking of making a struct to hold 4 bytes for length sending, 4 bytes for message ID and then a char* for the message. Then sending this over to the other end. However I came across two problems this way. (a) on the receiving end there is no way (that i can see) to pull out the correct size of the struct, to gain access to the length field. I was thinking of sizeof(struct) but that would only return 8 bytes as the char* would only be described as 4 bytes. So how would I recieve the entire struct? so on to (2) A char* with 4 bytes describign length 4 bytes on messageid then following is the message. Then I test if the incoming is leftover data needed to complete last message, or a new one. If new one i pull out length, and message id, then parse through the message 1 char at a time transfering it into a vector, until len has been reached. If any is left over, then a int value is assigned with remander and waits for next receive to pull in rest. here is example flowchart. receive char* msg if (new message) //msglength equals 0 pull out first 4 bytes and assign to length pull out next 4 bytes and assign to messageID make messagelength equal to length loop through from 0 to end of message pulling in each character and reducing messagelength if (finish message) //msglength > 0 loop through from 0 till msglength equals 0 if length > msglength then goto new message Ok, now the problem with this is, what happens if the finished message ends near the end of the receving message, and the new message only starts with the first 4 bytes? Then it would all fail as you wouldnt be reading in the 4 bytes for messageID and instead it would treat that as par tof the message. This would very very infrequent happen, but could, if im right. So you would start to have to add in alot more checks (3) Do like most do and just assign a base size to the buffer, however what if you wanted less than this or more? so lastly my idea that I havent tried (4) linked lists Can you have a linked list where each "record" had a length and buffer and a link. then first full message would be assigned to the buffer in the linked list, and the size of it, then the first 4 bytes would be retrieved finding out the size fo the full message. If the full size is within the inital message then it is parsed accordingly, then checks to see if a new message is also started in initial message. If the message is not fully completed, then the next receive is made into the next record, and the first is linked to it, again checking for the full message. Once the message is compelted, the records containing the first messages are all deleted and the record with new message started remains, and the head is now linked to this. This way you can have a message a slarge as you want and only memory your using up is what is acually holding data. So will this work? Or am i missing an easier way or wrong about a previously mentioned one? please help teach me :)
You could write a Packet class and then serialize it and send it over the wire that way. This assumes you are using a language that supports this of course.
kezz
kezz
-------------------------0 A.D.
In C++ Serialization would be the way to go. You could implement a two part system. One class deserializes only part of the message to determine type and then sends it to a complex class to deserialize the rest for use by your program.
Thats how alot of games do it. In fact they do it much more directly by taking the first 1 to 4 bytes of the packet and making that be the packet type. That way you could write this: to determing type and do something about it.
switch(buffer[0])
{
case 0x01: //Packet type 1
break;
case 0x02: //Packet type 2
break;
default: //Unhandled packet type
break;
}
Thats how alot of games do it. In fact they do it much more directly by taking the first 1 to 4 bytes of the packet and making that be the packet type. That way you could write this: to determing type and do something about it.
switch(buffer[0])
{
case 0x01: //Packet type 1
break;
case 0x02: //Packet type 2
break;
default: //Unhandled packet type
break;
}
i understand about first 4 bytes being your id or length (in whatever order you want) but how would you set your receive to pull in the data efficently? and able to increase or decrease the length?
This is actually covered explicitly in the Forum FAQ. I think it's question 15 (on tokenization over TCP).
In brief, you need to call recv() twice; once for the known header size, and once for the rest of the data. So:
In brief, you need to call recv() twice; once for the known header size, and once for the rest of the data. So:
struct Header { unsigned int msg; unsigned int size;};struct Packet { Packet() { data = 0; h.msg = 0; h.size = 0; } ~Packet() { free( data ); } Packet( Packet const & o ) { ... } Packet& operator=( Packet const & o ) { ... } Header h; void * data;};/// send_packet is used on the sending side to send a packetvoid send_packet( Packet const & p, SOCKET s ) { if( ::send( s, sizeof(p.h), &p.h, 0 ) != sizeof(p.h) ) { throw BadSend; } if( p.h.size > 0 && ::send( s, p.h.size, p.data, 0 ) != p.h.size ) { throw BadSend; }}/// recv_all is a convenient wrapper to ensure receiving /// enough data on a reliable stream.bool recv_all( SOCKET s, size_t z, void * d ) { while( z > 0 ) { int r = ::recv( s, z, d ); if( r < 1 ) return false; z -= r; d = (void *)(((char *)d)+r); } return true;}/// recv_packet is used on the receiving side to receive /// a packet.void recv_packet( Packet * oP, SOCKET s ) { if( !recv_all( s, sizeof(oP->h), &oP->h ) ) { throw BadRecv; } oP->data = realloc( oP->data, oP->h.size ); if( oP->h.size > 0 && !recv_all( s, oP->h.size, oP->data ) ) { throw BadRecv; }}
enum Bool { True, False, FileNotFound };
thanks hplus on the detailed code to help.
I am receiving problems in accualyl receiving
the header and the data however. Here is my code
for sending and receiving, then my logfile of results
on client (server just reports both were sent successful)
If you can spot the problem, I would be greatly apprecitative!
server send code:
and here is setting the variables in server:
Packet test;
test.data = "this is a test";
test.h.size = 14;
test.h.msg = 1;
myHandle.send_packet(test);
here is struct's as defined gloably in both client and server
here is Client code for receiving the packets
WriteError writes a char* to a file for debugging. Here is the file contents that relates to this problem:
*note not sure if seen, but the first response in logfile is a return of an empty char*
I am receiving problems in accualyl receiving
the header and the data however. Here is my code
for sending and receiving, then my logfile of results
on client (server just reports both were sent successful)
If you can spot the problem, I would be greatly apprecitative!
server send code:
void MessageHandler::send_packet(Packet &p){ if(send(client,(char*)&p.h,sizeof(p.h),0) != sizeof(p.h)) { WriteError("Bad Send of packet info"); return; } else WriteError("success on sending header"); if(p.h.size > 0 && send(client,(char*)p.data,p.h.size,0) != p.h.size) { WriteError("Bad Send of packet, Size did not match"); return; } else WriteError("success on sending packet");}
and here is setting the variables in server:
Packet test;
test.data = "this is a test";
test.h.size = 14;
test.h.msg = 1;
myHandle.send_packet(test);
here is struct's as defined gloably in both client and server
struct Header{ unsigned int msg; unsigned int size;};struct Packet{// Packet() {data = 0;h.msg=0;h.size=0;}// ~Packet() {free(data);} Header h; void *data;};
here is Client code for receiving the packets
bool recv_all(void *d,size_t z){ while(z > 0) { int r = recv(st,(char*)d,z,0); WriteError((char*)d); if(r < 1) { WriteError("failed on return value within recv_all"); return false; } else WriteError("success in loop within recv_all"); z -= r; d = (void *)(((char*)d)+r); } return true;}void recv_packet(Packet *p){ if(!recv_all(&p->h,sizeof(p->h))) { WriteError("failed on return from recv_all for header"); } else WriteError("Header received"); p->data = realloc(p->data,p->h.size); if(p->h.size > 0 && !recv_all(p->data,p->h.size)) { WriteError("failed on second return from recv_all for data"); } else WriteError("Data received"); receivePlayerStruct = false;}
WriteError writes a char* to a file for debugging. Here is the file contents that relates to this problem:
success in loop within recv_allHeader receivedsuccess in loop within recv_allÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««îþîþfailed on return value within recv_allfailed on second return from recv_all for data
*note not sure if seen, but the first response in logfile is a return of an empty char*
You should print the actual return values (i e, "r") in addition to the text. Also, writing the text of the header isn't going to work right, as the header is binary data. Last, you're calling realloc() on the "data" member in receive, and it's not clear that the "data" member is initially NULL or previously returned from malloc().
Another question: is your socket non-blocking? If so, you have to be smarter about the buffering than the recv_all() function; it assumes the socket is blocking. To receive packets over a stream socket that's non-blocking, you have to build a small state machine on top of a separate buffering area, to receive whatever data is available, and then see whether it's a complete message yet or not.
Usage of the above code sketch (not compiled or tested) is such that each time through your main loop, you poll the receive state machine, and if it returns a packet pointer, you can use that packet until the next time you poll the receive state machine again.
Another question: is your socket non-blocking? If so, you have to be smarter about the buffering than the recv_all() function; it assumes the socket is blocking. To receive packets over a stream socket that's non-blocking, you have to build a small state machine on top of a separate buffering area, to receive whatever data is available, and then see whether it's a complete message yet or not.
class ReceiveBuffer { public: ReceiveBuffer( size_t maxSize = 1024 ) { base_ = new char[maxSize]; end_ = base_+maxSize; ptr_ = base_; } ~ReceiveBuffer() { delete[] base_; } void poll( SOCKET s ) { int r = ::recv( s, ptr_, end_-ptr_, 0 ); if( r > 0 ) { ptr_ += r; } } size_t size() const { return ptr_-base_; } char const * data() const { return base_; } void remove( size_t s ) { assert( s <= size() ); ::memmove( base_, base_+s, size()-s ); ptr_ -= s; } size_t maxSize() const { return ptr_-base_; } char * base_; char * end_; char * ptr_;};class ReceiveStateMachine { public: ReceiveStateMachine( SOCKET s ) : s_( s ), gotHdr_(false) { packet_.data = 0; } Packet * poll() { if( packet_.data ) { assert( gotHdr_ ); gotHdr_ = false; packet_.data = 0; buf_.remove( packet_.h.size ); } buf_.poll( s_ ); if( !gotHdr_ && buf_.size() >= sizeof(packet_.h) ) { ::memcpy( &packet_.h, buf_.data(), sizeof(packet_.h) ); buf_.remove( sizeof(packet_.h) ); gotHdr_ = true; if( packet_.h.size >= buf_.maxSize() ) { throw NetError( "junk input data" ); } } if( gotHdr_ && buf_.size() >= packet_.h.size ) { packet_.data = buf_.data(); return &packet_; } return 0; } SOCKET s_; ReceiveBuffer buf_; bool gotHdr_; Packet packet_;};
Usage of the above code sketch (not compiled or tested) is such that each time through your main loop, you poll the receive state machine, and if it returns a packet pointer, you can use that packet until the next time you poll the receive state machine again.
enum Bool { True, False, FileNotFound };
Well the problem with the header was big endian/little endian (sp?) where I had to take the values received on the client, and cast them as htons , then they came out fine.
Still getting garbage though for the data, but at least the size is coorect received so not overflowing the buffer anymore.
I am thinking this could be what you were saying hplus, where i am running a non-blocking socket on the client side. But if the first receive call pulled in the header as well as the data, then would the data shown on the second one show garbage? Shouldn't it show NULL as it was reallocated?
Also, on the big/small endian(sp?) I still have a question. Why did it switch them around when I compiled and ran both server and client on my machine? The archetecture isnt diff, so why would they be switched? Does winsock using tcp/ip make these into another format automatically like utf? So you would have to cast them back?
Still getting garbage though for the data, but at least the size is coorect received so not overflowing the buffer anymore.
I am thinking this could be what you were saying hplus, where i am running a non-blocking socket on the client side. But if the first receive call pulled in the header as well as the data, then would the data shown on the second one show garbage? Shouldn't it show NULL as it was reallocated?
Also, on the big/small endian(sp?) I still have a question. Why did it switch them around when I compiled and ran both server and client on my machine? The archetecture isnt diff, so why would they be switched? Does winsock using tcp/ip make these into another format automatically like utf? So you would have to cast them back?
OK I have started a thread at codeguru as well, and soem people are havign a crack at it, i have tried several variations of the receive and send functions. All results and ideas are at the folling url
Click here to be directed to CodeGuru forum on specific topic
if you dont mind having a peak over there and either responding here or there, i am checking both frequently. Any more suggestions or help would be GREATLY HELPFUL as i feel like hitting my head against a brick wall at this point :)
Click here to be directed to CodeGuru forum on specific topic
if you dont mind having a peak over there and either responding here or there, i am checking both frequently. Any more suggestions or help would be GREATLY HELPFUL as i feel like hitting my head against a brick wall at this point :)
Quote:
, on the big/small endian(sp?) I still have a question. Why did it switch them around when I compiled and ran both server and client on my machine?
I'm pretty sure that's not the problem -- the data you send in will be exactly the same data (byte order wise) you pull out. Thus, if you send and receive on the same machine, then the data should be the same.
Write a very small test program that just sends three integers. Put in three values like 42, 12345678 and -12345678. Print a hex dump of your entire data block before you send() it. Print a hex dump of the data block when you recv() it, and compare.
Code to dump data as hex (untested, but ought to work):
void dump_as_hex( char const * label, void const * data, size_t size ) { fprintf( stderr, "%ld bytes of %s:\n", size, label ); unsigned char * ch = (unsigned char *)data; size_t done = 0; while( done < size ) { if( done && !(done & 15) ) { fprintf( stderr, "\n" ); } fprintf( stderr, " %02x", ch[done] ); ++done; } fprintf( stderr, "\n" );}
enum Bool { True, False, FileNotFound };
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement