Lefsler
Lefsler

Reputation: 1768

Select on socket messes up with data

I'm sending some data trough the socket, but I need to set the timeout.
I'm using something like:

fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(sockDesc, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(sockDesc + 1, &rfds, NULL, NULL, &tv);
if(rtn = ::recv(sockDesc, (raw_type*) buffer, bufferLen, 0)) < 0){
throw SocketException("error", true);
}
return rtn;

So.
Depending the type of data I return I need to implement timeout or not.
If I just send text data I don't need, if I send one file I need...
To explain more or less I'm sending some data through a socket and processing on the other side.
So, if I send one tar:

while(readtar){

  senddata
  get processed data
}

but sometimes the data sent is just the header, so when the other side process data it doesn't needs to return data and the socket stops on read.
To illustrate: cat file.tar | myprogram -c "tar -zvt"

So it don't return nothing until receive enough data to return the name of file.

If i just send one file and returns the "cat" i dont have this problem
echo "asjdoiajdlaijdkasdjlkas" | myprogram -c "cat"
or
cat HUGEFILE.tar | myprogram -c "cat" | tar -zvt
In this case it does the same thing, but is not on the server side... so it dont work for me.

Now.... If I just use the recv without the select when I return the data using the cat it works... no problems with that.
BUT if I implement the select the data comes messed up.

WITHOUT SELECT

send "command line text temp test"
recv "command line text temp test"

WITH

send "command line text temp test"
recv "commmand lin/ˆ
1k5d99ck"

it's just to illustrate what is happening

Client loop:

while(size = read(fileno(stdin), thebuffer, 10000)){
  sock->send(thebuffer, size); // if this data is not enough the other side never sends the data back
  sock->recv //receive data
}

On the other side I do

if(pid == 0){
//stuffs closes, dup2
execlp("bash", "bash", "-c", "run.c_str(), NULL); // if i use one tar -zvt i need a bunch of data to generate the return
  }
else
while(size = read(fout[0], buffer, 10000) > 0)){
  sock->send(buffer, size);
}

So.. if the data sent is not enough to generate on If I could check if read have anything or on the stdin side if the execlp send a terminator I could solve the problem

Upvotes: 1

Views: 425

Answers (1)

Jeremy Friesner
Jeremy Friesner

Reputation: 73041

It sounds like you are expecting your TCP recv() to return data to you in chunks of a particular size... however, TCP recv() does not work that way. TCP is stream-based, so the number of bytes returned by recv() may vary anywhere between 1 (or 0 if you are using non-blocking I/O) and the size of the buffer you passed in. It's then up to your receiving code to loop as necessary to re-concatenate the received data again.

Also, it looks like you are trying to print out un-terminated ASCII strings -- that would explain the garbage characters at the end of your second recv() example. (i.e. if you want to print out the received data bytes as a string, be sure to place a NUL/0 byte after the last received byte; recv() won't do that for you)

Upvotes: 4

Related Questions