whats wrong with the socket recv

I have a lite composite control which is going to download data from server to client machine.

Each time, it receives 8 unsigned short type of numbers as the header of the data, then receive one chunck of data.

I have the code below. Sometimes I got the data but in the middle the socket was closed before getting the last header, and the received header was messed up after the socket was closed.

Could some nice guru tell me what's wrong with the Receive thread?

UINT CDLCtrl::Receive(LPVOID arg)
{

char recvsize[2];
char recvMessage[40000];
char framemarker[500];
unsigned short recvbytes=0;
unsigned short recvnum=0;
unsigned short leftsize=0;
unsigned short debug=0;
unsigned short year,month,day,hour,min,sec,usec;
int count=0;
FILE * fp;

CDLCtrl *myctrl= (CDLCtrl*)arg;

fp=fopen(myctrl->saveFileName,"wb");



while(1){
while(count<8){
count++;
if((recvbytes=recv(myctrl->sock, (char*)&debug, sizeof(unsigned short), 0)) == SOCKET_ERROR)
{
int res = WSAGetLastError();
AfxMessageBox("Error: recv() failed.");
return 1;

}
else
{
recvnum=ntohs(debug);

}

switch(count){
case 1:
month=recvnum;
break;

case 2:
day=recvnum;
break;

case 3:
year=recvnum;
break;

case 4:
hour=recvnum;
break;
case 5:
min=recvnum;
break;

case 6:
sec=recvnum;
break;

case 7:
usec=recvnum;
break;

case 8:
leftsize=recvnum;

break;

}
}


count=0;

if(leftsize==0){
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "End", NULL,MB_OK);
return 0;

}


if(year!=0){
sprintf(framemarker,"--frame;%s;%d/%d/%d;%d:%d:%d:%d;%d\n","Lab-Entry",year,month,day,hour,min,sec,usec,leftsize);

fputs(framemarker,fp);
}


while(leftsize>0){

if((recvbytes=recv(myctrl->sock, recvMessage, leftsize, 0)) == SOCKET_ERROR)
AfxMessageBox("Error: recv() failed.");
else
{
leftsize-=recvbytes;


if(year!=0)
fwrite(recvMessage,1,recvbytes,fp);

}
}

if(year!=0)
fputs("\n",fp);

}

closesocket(myctrl->sock);
fclose(fp);
return 0;
}
[2963 byte] By [hihiforever] at [2007-11-17 11:49:11]
# 1 Re: whats wrong with the socket recv
As far as I can see... you are specting to receive the whole data in one receive call.

Since it is an asyncronic operation, it is possible that you have to call recv() more than once in order to get all the data you are wating for.

Usually, you have to poll the socket status to know when data is available. When it happens you may call recv() as long as is data left in your socket. When you get all your data... you have to poll again.

You have to implement a propietary protocol to be able to know when your data is complete. (ie: you may have fixed size data packets and store the data received with recv() in a buffer until this buffer reaches your data packet size. Then, you may parse your data.

---------
_Leo_
http://www.drk.com.ar
_Leo_ at 2007-11-10 8:06:50 >
# 2 Re: whats wrong with the socket recv
Thanks for your response.

But maybe you didn't notice, I used a while(leftsize>0) loop to see whether I get the whole chunck of data.

What I did was fwrite whatever recvbytes I got from one recv call to the file, then loop back to check out whether there are any bytes left until I got one full chunk of data.

The complete condition was if I got the data size equal to zero(which is the 8th unsigned short number of the header, if(leftsize==0)), then the thread will stop receiving.

What I didn't understand was why the server side disconnected the socket before the client side received the stop condition.

What I am doing is like continuously receiving the packet header(which are 8 unsigned short numbers), then the packet data, and write it to a file. Until the newly received header meet the stop condition.
hihiforever at 2007-11-10 8:07:51 >
# 3 Re: whats wrong with the socket recv
I see...

...
while(1) {
while(count<8){
count++;
if ((recvbytes=recv(myctrl->sock, (char*)&debug, sizeof(unsigned short), 0)) == SOCKET_ERROR) {
int res = WSAGetLastError();
AfxMessageBox("Error: recv() failed.");
return 1;
}
else {
recvnum=ntohs(debug);
}

switch(count) {
case 1:
month=recvnum;
break;
...

Here recv() function may return 0 if the connection is closed by the other side.

Correct your condition to halt on 0 being returned by recv()

---------
_Leo_
http://www.drk.com.ar
_Leo_ at 2007-11-10 8:08:51 >
# 4 Re: whats wrong with the socket recv
Thanks again.

I tried to recv one byte at a time for the header,
it somehow worked on most of the packets except the last one. the last packet's header has 7 zero fields just as same as the first packet's header, but it only worked on the first packet, not the last packet. Could you help me figure out why?

Below are the modified code:

UINT CDLCtrl::Receive(LPVOID arg)
{
int i=0;
char recvsize[2];
char recvMessage[40000];
char framemarker[500];
unsigned short recvbytes=0;
unsigned short recvnum=0;
unsigned short leftsize=0;
unsigned short year,month,day,hour,min,sec,usec;
int count=0;
FILE * fp;

CDLCtrl *myctrl= (CDLCtrl*)arg;

fp=fopen(myctrl->saveFileName,"wb");

recvMessage[0]='\0';

recvsize[0]='\0';

while(1){
while(count<8){
count++;
if((recvbytes=recv(myctrl->sock, (char*)&recvsize[0], 1, 0)) == SOCKET_ERROR)
{
int res = WSAGetLastError();
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "recv() failed","Error", MB_OK);
return 1;

}
if(recvbytes==0){
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Server socket closed", "Message",MB_OK);
return 1;
}
else if(recvbytes==1)
{
if((recvbytes=recv(myctrl->sock, (char *)&recvsize[1], 1, 0)) != SOCKET_ERROR){
if(recvbytes==1)
recvnum=((recvsize[0]&0xff)<<8)|(recvsize[1]&0xff);
else if(recvbytes==0){
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Server socket closed", "Message",MB_OK);
return 1;
}
}
else{
::MessageBox(NULL, "recv() failed","Error", MB_OK);
return 1;
}

}


switch(count){
case 1:
month=recvnum;
break;

case 2:
day=recvnum;
break;

case 3:
year=recvnum;
break;

case 4:
hour=recvnum;
break;
case 5:
min=recvnum;
break;

case 6:
sec=recvnum;
break;

case 7:
usec=recvnum;
break;

case 8:
leftsize=recvnum;

break;

}
}


count=0;

if(leftsize==0){
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Downloading Completed", "Message",MB_OK);
return 0;

}


if(year!=0){
sprintf(framemarker,"--frame;%s;%d/%d/%d;%d:%d:%d:%d;%d\n","Lab-Entry",year,month,day,hour,min,sec,usec,leftsize);

fputs(framemarker,fp);
}


while(leftsize>0){

if((recvbytes=recv(myctrl->sock, recvMessage, leftsize, 0)) == SOCKET_ERROR)
{
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "recv() failed","Error", MB_OK);
return 1;
}
else
{
leftsize-=recvbytes;
if(recvbytes==0){
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Server socket closed", "Message",MB_OK);
return 1;

}

else if(year!=0){
fwrite(recvMessage,1,recvbytes,fp);

}
}
}

if(year!=0){
fputs("\n",fp);
}

}

closesocket(myctrl->sock);
fclose(fp);
return 0;
}
hihiforever at 2007-11-10 8:09:45 >
# 5 Re: whats wrong with the socket recv
mmm... it seem too complex for what you need... try doing something like this:

UINT CDLCtrl::Receive(LPVOID arg) {
...
char buffer[8];
int recvbytes;
FILE * fp;
...
...
CDLCtrl *myctrl= (CDLCtrl*)arg;
...
fp=fopen(myctrl->saveFileName,"wb");
...
// Following code should read one packet...
...
// Get 8 (unsigned short) from socket
if ((recvbytes = recv(myctrl->sock, buffer, sizeof(buffer), 0)) == SOCKET_ERROR) {
int res = WSAGetLastError();
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "recv() failed","Error", MB_OK);
return 1;
}

if (recvbytes == 0) {
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Server socket closed", "Message", MB_OK);
return 1;
}

if (recvbytes != 8) {
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Can't get full packet", "Message", MB_OK);
return 1;
}

// So far we should have received 8 bytes
if (recvbytes == 8) {
// At this point you may access buffer[n] to get
// n = 0 -> month
// n = 1 -> day
// n = 2 -> year
// n = 3 -> hour
// n = 4 -> min
// n = 5 -> sec
// n = 6 -> usec
// n = 7 -> leftsize

// For debuging
char string[256];
sprintf(str, "Packet: %d - %d - %d - %d - %d - %d - %d - %d", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
::MessageBox(NULL, str, "Debug", MB_OK);

// Add your storage code here...

}
else {
fclose(fp);
closesocket(myctrl->sock);
::MessageBox(NULL, "Unspected error reading socket", "Message", MB_OK);
return 1;
}

// Normal exit
return 0;
}

I think using this code you will make your debuging job a little easier.

PD: When positing code, enclose it using "ccode" and "/ccode" tags, it will be easier to read that way.

---------
_Leo_
http://www.drk.com.ar
_Leo_ at 2007-11-10 8:10:47 >
# 6 Re: whats wrong with the socket recv
Thanks for your advice. I have solved the problem.
For the unsigned short, it was two bytes, at the beginning, I tried to recv two bytes together, now I fixed it with receiving one byte at a time.
Appeciate your help.
hihiforever at 2007-11-10 8:11:48 >