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 2011-02-25 12:32 PM

pankajdev
Member
Registered: 2009-07-15
Posts: 14

splice

I am currently doing r&D as how to use splice to write and receive files from the socket .

Here is my code to send data from a file to socket

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h> 
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

struct MyFds
{
   int fdin;
   int fdout;
   int readpipe;
   int writepipe;
   loff_t size;
   MyFds(int p_fdin, int p_fdout, int p_readpipe, int p_writepipe,
loff_t p_size) :
       fdin(p_fdin), fdout(p_fdout), readpipe(p_readpipe),
writepipe(p_writepipe), size(p_size)
       {

       };
};

static void * splicecopywriter(void * threadparam)
{
   const MyFds * fds = (const MyFds *) threadparam;
   std::cout << "In writer thread " << std::endl;
   loff_t offset = 0;
   size_t bytesleft = fds->size;
   int splices=0;
   while (bytesleft > 0) {
       splices ++;
       ssize_t bytes = splice(fds->readpipe, (loff_t *) 0,
fds->fdout, & offset,
                          bytesleft, 0 /* flags */ );
       if (bytes == -1) {
           break;
       }
       bytesleft -= bytes;
   }
   int spliceerr = errno;
   std::cout << "writer:splices= " << splices << " errno=" <<
spliceerr << std::endl;
   std::cout << "still in writer thread " << std::endl;
   return (void *) 0;
}

static int splicecopyreader(const MyFds *fds)
{
   loff_t offset = 0;
   size_t bytesleft = fds->size;
   int splices = 0;
   while (bytesleft > 0) {
       splices ++;
       ssize_t bytes = splice(fds->fdin, &offset, fds->writepipe,
(loff_t *) 0,
                          (size_t) fds->size, 0 /* flags */ );
       if (bytes == -1) {
           break;
       }
       bytesleft -= bytes;
   }
   int spliceerr = errno;
   std::cout << "reader: splices=" << splices << " errno=" <<
spliceerr << std::endl;
   close(fds->writepipe);
}

static int splicecopypipes(const MyFds *fds)
{
   // Main thread reads from disc and into pipe;
   // writerthread reads from pipe and on to disc.
   pthread_t writerthread;
   int res = pthread_create(&writerthread,
                            (const pthread_attr_t *) 0,
                            splicecopywriter,
                            (void *) fds);

   std::cout << "In main thread" << std::endl;
   splicecopyreader(fds);
   int threadres;
   pthread_join(writerthread, (void **) (& threadres) );
   std::cout << "Joined" << std::endl;
   return threadres;
}

static int splicecopyfd(int fdin, int fdout)
{
   // Work out how big it is
   struct stat st;
   if (fstat(fdin, &st) != 0) {
       std::cerr << "Stat failed" << std::endl;
       return 3;
   }

   int pipes[2];
   if (pipe(pipes) != 0) {
       std::cerr << "Pipe failed" << std::endl;
       return 4;
   }
   int readpipe = pipes[0];
   int writepipe = pipes[1];
   MyFds fds(fdin, fdout, readpipe, writepipe, st.st_size);
   int res = splicecopypipes(&fds);
   close(pipes[0]);
   close(pipes[1]);
   return res;
}

static int splicecopy(const char *srcfile)
{
   int fdin = open(srcfile, O_RDONLY);
   if (-1 == fdin) {
       std::cerr << "Failed to open src file" << std::endl;
       return 1;
   }

   struct sockaddr_in lstServerAddr.
   inet_aton("172.20.101.38", &(lstServerAddr.sin_addr));
   lstServerAddr.sin_port = htons(8000);
   lstServerAddr.sin_family = AF_INET;

   int fdout = socket(AF_INET, SOCK_STREAM, 0);
   if (fdout == -1)
  {
      printf("failed due to %s", strerror(errno));
      return 1;
  }

  if (connect(fdout, (struct sockaddr*)&lstServerAddr, sizeof(sockaddr_in)) == -1)
  {
      printf("connect failed due to %s", strerror(errno));
      return 1;
  }


   int res = splicecopyfd(fdin, fdout);
   close(fdin);
   close(fdout);
   return res;
}

int main(int argc, const char * argv[])
{
   if (argc < 2) {
       std::cerr << "Source required" << std::endl;
       return 1;
   }
   const char *srcfile = argv[1];
   return splicecopy(srcfile);
}

while writing to the socket it gives error as Invalid argument ie in  the writer thread it give error Invalid Argument (EINVAL).
I have also tried  setting  both the offset to null while sending that doesn't work.

Please suggest as what os wrong exactly.

Thanks
Pankaj

Offline

#2 2011-02-25 02:22 PM

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

Re: splice

You've really tried using NULL for all offsets?  Because, that would be my guess as to your problem...  Specifically, trying to specify an offset for the socket, which is non-seekable...

Also, I found when using splice(), it was wise to limit single calls to no more than about 60K at a time, due to the pipe buffer size...  And, when copying to a socket, performance is greatly improved by using the SPLICE_F_MORE flag when you know you have more to write still to come...  And, when doing a double-splice() with your own pipe (to copy between non-pipe FDs) as you're doing, you'd also be well-advised to add SPLICE_F_MOVE in the second splice() (the one copying from the pipe to the socket)...

Offline

#3 2011-02-26 08:10 AM

pankajdev
Member
Registered: 2009-07-15
Posts: 14

Re: splice

Hi Robseace,

you were right i rechecked the code and it worked fine.
and as per your advise i have set the flag to SPLICE_F_MORE | SPLICE_F_MOVE in the second splice (coping from pipe to socket).

Thank you for the reply.

Regards,

Nithesh

Offline

#4 2011-02-26 10:24 AM

pankajdev
Member
Registered: 2009-07-15
Posts: 14

Re: splice

I have sucessfully send data from a file to a socket using splice. Now while receiving / writing data from a socket to file using socket it
gives me EWOULDBLOCK error.

here is the Sample code.

int ReadData(int nSocketFD, int FileFd, int readpipe, int writepipe)
{
    int lnRetValue = 0;
    loff_t lSocketOffSet = 0;
    loff_t lFileOffSet = 0;
   while(true)
   {
       lnRetValue = splice(lnFd, &lSocketOffSet/*NULL*/, writepipe, (loff_t*)0, 1024, 0) ;  
       if (lnRetValue > 0)
       {
            if (lnRetValue == 1024)
           {  
               break;
           }
           else 
           {
                lnRetValue += lnRetValue;
           }
       }
       else if (lnRetValue == -1)
       { 
            printf("Splice failed due to %s", strerror(errno));
            return 1;
       }
   }

   lnRetValue = splice(readpipe, (loff_t*)0, lnFileFd, &lFileOffSet, 1024, 0);
   if (lnRetValue < 0)
   {
        printf("splice 1 failed due to %s", strerror(errno));
        return 1;
   }
 
return 0;
}

pipe used are non blocking. tried setting the socketoffset to NULL.

Please advise as what am i doing wrong.

Regards,

Pankaj

Offline

#5 2011-02-26 08:34 PM

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

Re: splice

Please surround blocks of code with the "code" tags...

pipe used are non blocking.

So, then you should be expecting EAGAIN/EWOULDBLOCK...  That's normal behavior in non-blocking I/O mode...  You're supposed to select()/poll() until the FD becomes readable/writable in such a situation, and then try the I/O again...  That's how non-blocking I/O works...  If you don't want to deal with that extra work, then use blocking mode...

tried setting the socketoffset to NULL.

That's required...  There's really no reason to ever pass anything other than NULL for the offsets, unless for some strange reason you don't wish to change the current position within the FD...  But, that only works on seekable FDs, which neither sockets nor pipes are, so you can never use an offset with them...

Also, that code doesn't really look right...  Where is "lnFd" defined?  I don't see it anywhere...  Is it a global you don't show, or something?

Offline

#6 2011-02-27 06:02 AM

pankajdev
Member
Registered: 2009-07-15
Posts: 14

Re: splice

Robseace wrote:

Also, that code doesn't really look right...  Where is "lnFd" defined?  I don't see it anywhere...  Is it a global you don't show, or something?

thats type error. cannot copy paste the code to this site. some restriction over here.

i have used epoll and i read from socket only after i receive epollin event.

but now changed the code

lnRetValue = splice(nSocketFd, NULL, writepipe, (loff_t*)0, 1024, 0) ;

but now it gives me invalid argument error while reading from socket to the writepipe.


Thanks,

Nithesh

Offline

#7 2011-02-27 05:01 PM

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

Re: splice

Unless I can see the real code (and all of it), there's not really any way I can know what you're doing wrong...  If you're getting EINVAL and truly are passing NULL for both offsets, then I'd have to guess your write pipe isn't really a pipe, or has been closed, or something like that... *shrug*  All I can do is guess randomly without seeing all the real code which is actually producing the problem...

Offline

#8 2011-02-28 12:25 PM

pankajdev
Member
Registered: 2009-07-15
Posts: 14

Re: splice

Hi RobSeace,

Just found out that splicing from socket fd to a pipe is supported in Kernel version 2.6.25 where as our version is 2.6.18.

Need to change to 2.6.25.

Regards,
Pankaj

Offline

Board footer

Powered by FluxBB