UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

#1 2003-04-07 10:26 AM

x33
Member
From: Moscow, Russia
Registered: 2003-04-07
Posts: 20

Re: partial send() and recv()

Hi! I was wondering whether it is possible to force the kernel to send whole buffered data through a TCP socket? For example, how can i be sure that the buffer of 100 bytes is sent in one packet (the value returned by the send() call is equal to '100') and that on the other side of the connection the recv returns '100'? I really need to make a stream-based protocol to function as a datagram-based one... That's because user datagram protocol is not supported by the most popular proxy servers (SOCKS 4)... And I want my app to be proxy-compatible. That's why i decided to move my app to TCP... Any suggestions?
I know there's a simple way of doing it:

int Send (int sockfd, void* msg, size_t len)
{
  size_t n = 0;
  char* ptr = (char*)msg;
  while ( n < len ) 
  {
    int sent = send (sockfd, ptr+n, len-n, 0);
    if ( sent < 0 )
    {
      /* Some error occured during send */
      throw "send error";
    }
  n += sent;
  }
  return n;
}

int Recv (int sockfd, void* buf, size_t len)
{
  size_t n = 0;
  char* ptr = (char*)buf;
  while (n < len)
  {
    int recvd = recv (sockfd, ptr+n, len-n, 0);
    if (recvd < 0) 
    {
      /* Error */
      throw "recv error";
    }
    n += recvd;
  }
  return n;
}

but I wanted to know whether it is possible to make it through standart socket API calls (setting some socket-level options, perhaps)...


"I'll be Bach" (c) Johann Sebastian Schwarzenegger

Offline

#2 2003-04-07 01:50 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,753
Website

Re: partial send() and recv()

No, there's no way to force TCP to send data...  It'll send it when it
gets good and ready... ;-)  The best you can do is set TCP_NODELAY,
which will reduce the wait to the pure minimum you can hope for...

But, you'll always still need to wrap your send()/recv() like that, anyway...
Though, you probably want to allow a retval of <0 with errno EINTR to
be just silently ignored, since it's a relatively 'normal' condition...  And,
in your recv() cover, you'll want to handle a retval of exactly 0, which
means no more data is ever coming, because the connection is closed...
(Your code would currently go into an infinite loop if the connection were
shut down in the middle of receiving a message...)

As for sending pseudo-datagrams over TCP...  If they're all of known
fixed sizes, its easy enough: just use those send()/recv() wrappers to
process the exact amount...  But, if the sizes are variable, and the
receiver might not know in advance what the size of any incoming
message might be, then the best approach is to precede each message
with a small header consisting of the length of the upcoming message...
So, then, you can always recv() on a known fixed amount (your header
size), then from that, you'll now the full amount to recv() for the real
message...

Offline

#3 2003-04-07 02:23 PM

emihaly
Member
Registered: 2002-08-01
Posts: 191

Re: partial send() and recv()

But,
thanx to this scheme you are able make application without blocking while you receive all data.

Offline

#4 2003-04-08 09:59 AM

x33
Member
From: Moscow, Russia
Registered: 2003-04-07
Posts: 20

Re: partial send() and recv()

One more thing... =)
What if I use recv() with MSG_PEEK flag? For example, if we had such a piece of code:

int received_bytes = recv (sd, buffer, buffersize, MSG_PEEK);


and another portion of data is received the first time we call recv(), we would have 'received_bytes' set to the amount of bytes pending to receive. But what if we another portion of data is received, and we call recv() with MSG_PEEK once again? Would the 'received_bytes' variable be set to the total amount of data awaiting to be received, or would it be the same? If the return of the recv() is changed everytime the data arrives we could fight partial recv() with the MSG_PEEK flag then. We could do it like this:

int received_bytes = 0;
while (!(received_bytes >= buffersize))
        received_bytes = recv (sd, buffer, buffersize, MSG_PEEK);
int return = recv (sd, buffer, buffersize, 0);


That's it. We've got our data 'buffersize'-bytes long. What do you think of it?

Now... How do we fight the partial send()? I think I've got an idea...
What if we cast the socket_descriptor to file descriptor and fsync it after every send() call? We could do it like this:

int error_checking = send (casted_sd, buffer, buffersize, 0);
fsync (casted_sd);

Any comments or suggestions?


"I'll be Bach" (c) Johann Sebastian Schwarzenegger

Offline

#5 2003-04-08 01:29 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,753
Website

Re: partial send() and recv()

The fsync() trick won't do a thing for you...  It's only for flushing files
to disk...  It won't do anything to a socket...  It's TCP that decides when
to send socket data, and absolutely NOTHING you can do from a user-space
app perspective can ever force it to send data if it doesn't want to...  As
I said, the best you can do is set TCP_NODELAY...  That's it...  It's out of
your hands from then on, and TCP will send the data when it thinks it is
appropriate...

As for your MSG_PEEK idea...  Sure, why not...  That will work to get you
a fixed sized read...  However, I don't really see the point of using it instead
of something more like what you first posted...  With the MSG_PEEK
approach, I think you'd necessarily have to perform at least one extra
recv(), so it would work out to be more inefficient than the normal
approach...  (Plus, you're not testing for retval <= 0 in your MSG_PEEK
recv()...  Oh, and I must say that "!(received_bytes >= buffersize)" is
a most thoroughly bizarre and silly way to write "received_bytes < buffersize"... ;-))

Offline

#6 2003-04-08 06:35 PM

x33
Member
From: Moscow, Russia
Registered: 2003-04-07
Posts: 20

Re: partial send() and recv()

Thanks for your reply, Rob! =)
As for the '!(received_bytes >= buffersize)' i would say "oops!" =)... I've been working hard this day and sometimes my job turns my brain upside-down... that's the explanation. That was the most stupid piece of code i've ever posted. =)


"I'll be Bach" (c) Johann Sebastian Schwarzenegger

Offline

Board footer

Powered by FluxBB