http://www.perlmonks.org?node_id=197879

zentara has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Does anyone have an example of using perl sockets to transfer files? What modules would be best to use?

Replies are listed 'Best First'.
Re: File Transfer via Sockets
by gmpassos (Priest) on Sep 15, 2002 at 00:04 UTC
    Take a look in this scripts that I made to transfer files through different OS. This work fine on Perl-5.6.1. If you use Perl-5.8.0 you need to compile it withou PerlIO or set your enverioment variable PERLIO to :raw (maybe will crash with this) or the PerlIO layer will cause some erros with binary files.

    I use 2 scrips in the port 6123 (you can change), one client and one server. To use just run the server on the machine that will receive the files, and in the machine that will send, type this:

    perl client.pl file_to_send.ext host_name port

    CLIENT

    #!/usr/bin/perl #################### # SEND FILE CLIENT # #################### use IO::Socket ; $bandwidth = 1024*5 ; # 5Kb/s &send_file( $ARGV[0] , $ARGV[1]||'localhost' , $ARGV[2]||6123 ) ; exit; ############# # SEND_FILE # ############# sub send_file { my ( $file , $host , $port ) = @_ ; if (! -s $file) { die "ERROR! Can't find or blank file $file" ;} my $file_size = -s $file ; my ($file_name) = ( $file =~ /([^\\\/]+)[\\\/]*$/gs ); my $sock = new IO::Socket::INET( PeerAddr => $host, PeerPort => $port, Proto => 'tcp', Timeout => 30) ; if (! $sock) { die "ERROR! Can't connect\n" ;} $sock->autoflush(1); print "Sending $file_name\n$file_size bytes." ; print $sock "$file_name#:#" ; # send the file name. print $sock "$file_size\_" ; # send the size of the file to server. open (FILE,$file) ; binmode(FILE) ; my $buffer ; while( sysread(FILE, $buffer , $bandwidth) ) { print $sock $buffer ; print "." ; sleep(1) ; } print "OK\n\n" ; close (FILE) ; close($sock) ; } ####### # END # #######

    SERVER

    #!/usr/bin/perl #################### # SEND FILE SERVER # #################### use IO::Socket ; my $port = $ARGV[0] || 6123 ; my $save_dir = './files' ; ############# # START SRV # ############# if (! -d $save_dir) { mkdir($save_dir,0755) ; print "Save directory created: $save_dir\n" ; } my $server = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => $port , Proto => 'tcp' ) or die "Can't create server socket: $!"; print "Server opened: localhost:$port\nWaiting clients...\n\n" ; while( my $client = $server->accept ) { print "\nNew client!\n" ; my ($buffer,%data,$data_content) ; my $buffer_size = 1 ; while( sysread($client, $buffer , $buffer_size) ) { if ($data{filename} !~ /#:#$/) { $data{filename} .= $buffer ; +} elsif ($data{filesize} !~ /_$/) { $data{filesize} .= $buffer ;} elsif ( length($data_content) < $data{filesize}) { if ($data{filesave} eq '') { $data{filesave} = "$save_dir/$data{filename}" ; $data{filesave} =~ s/#:#$// ; $buffer_size = 1024*10 ; if (-e $data{filesave}) { unlink ($data{filesave}) ;} print "Saving: $data{filesave} ($data{filesize}bytes)\n" ; } open (FILENEW,">>$data{filesave}") ; binmode(FILENEW) ; print FILENEW $buffer ; close (FILENEW) ; print "." ; } else { last ;} } print "OK\n\n" ; } ####### # END # #######

    Graciliano M. P.
    "The creativity is the expression of the liberty".

      Hi just wanted to check how are you running server side script, is it running as a cron on the destination server? or is it supposed to be manually run on destination server once we are done with running client script of sender server?

Re: File Transfer via Sockets
by Zaxo (Archbishop) on Sep 14, 2002 at 16:02 UTC

    LWP has lots of high level protocols for that. No need to get your hands dirty on the low level socket stuff. If this is a learning exercise, look at Socket and IO::Socket.

    After Compline,
    Zaxo

Re: File Transfer via Sockets
by jryan (Vicar) on Sep 14, 2002 at 19:56 UTC

    You should also look at Net::FTP.

    If you really need to do it with straight sockets, stick with IO::Socket. It's much easier to use than Socket, and is also part of the core. To "transfer" a file via sockets, it would involve a process like this:

    1. From the sending side, connect to the recieving side.
    2. Send some sort of header to the recieving side, stating such like the file size, send chunk size, etc.
    3. The recieving side should recieve this, validate, and then send a response.
    4. The sending side should recieve the response, open the file in binary mode (default on *nix; use binmode for windows), and then split the file into chunks of the agreed size (probably somewhere around 1000 bytes, leaving room for a validation header if you need it.).
    5. Each chunk should be sent separately; the next chunk should be sent after the reciever sends an "ok".
    6. The recieving side should re-assemble the file by stripping the headers and then writing it to a temp file. After recieving the entire file, it should move the temp file to its properly location and rename it.

    Actual implementation details may vary, but you'd need to do something like this. I'd be much easier to use an existing protocol like FTP and an aforementioned module to compliment it.


    :^) # Fear the wrath of the hyper smiley!
Re: File Transfer via Sockets
by Ryszard (Priest) on Sep 15, 2002 at 08:30 UTC
    Mmmmmm, how about using CGI.pm's upload thingy:
    my $fh = $q->param('file'); my @name = split(/\./, $fh); open (OUT, ">/tmp/.$name[$#name-1].txt"); { my $buffer; my $fbuffer; while ( read($fh, $buffer, 1024) ) { print OUT $buffer; } }

    And now for the HTML:

    <table border="0" cellpadding="4" cellspacing="0" width="100%" bgcolor +="#e8e8e8"> <tr valign="center"> <form method="post" action="/cgi-bin/tools/app/nmpupload/nmpupload +.pl?rm=mode2" enctype="multipart/form-data"> <td width="30%"> <pre> NMP Data File:</pre> </td> <td align="right"><input type="file" name="file"> </td> </tr> <tr> <td> <input type="hidden" name="rm" value="mode2"> <input type="submit" value="Upload"> </form> </td> </tr> </table>

    Update: Fixed some missing curly braces...

Re: File Transfer via Sockets
by DigitalKitty (Parson) on Sep 15, 2002 at 04:07 UTC
Re: File Transfer via Sockets
by zentara (Archbishop) on Sep 15, 2002 at 15:07 UTC
    Hi, thanks for all the suggestions. I finally settled on using the Net::EasyTCP module. The pod contains a nice example. The reason I like it, is it has a built-in capability for password-protecting the socket, and has the options to compress and encrypt. These features would be hard to add to the simple socket scripts. I'm going to try and use gmpassos's techniques for the transfer, and incorporate them with Net::EasyTCP. When I get it working, I'll post them for you.