Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

client server file transfer

by Boots111 (Hermit)
on Jun 23, 2001 at 00:55 UTC ( [id://90861]=perlquestion: print w/replies, xml ) Need Help??

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

So I spent the last few hours trying to figure out how to get my client-server system (which now uses sockets) to transfer files. The final results is:

For the server:
sub SendFile { my($to, $file) = @_; print $to "Sending $file:\n"; open(IN, $file) || die("Could not open $file: $!\n"); binmode(IN); print $to <IN>; print $to "\nEND\n"; close IN; }
and for the client:
sub RecieveFile { my($server) = $_[0]; $line = <$server>; $line =~ /Sending (.*):/; print("Receiving $1...\n"); open(OUT, ">$1.cpy") || die("Cannot open $1.cpy: $!\n"); binmode(OUT); while(($_ = <$server>) ne "END\n") { print OUT; } close(OUT); print("Finished\n"); }
Originally I tried to do this without the little END tag; however that produced odd results. The client would seem to hang until the server was killed, at which point the client would recieve the file. Because I didn't know whether this one was should happen or not, I wrote the following test program:
use strict; open(IN, $ARGV[0]); binmode(IN); open(OUT, ">$ARGV[0].cpy"); binmode(OUT); print OUT <IN>; close(OUT); close(IN);
This program copies the file specified in its first agrument. Moreover this program works perfectly. Then I guessed that maybe (due to the client-server system) there was no way to specify that I had finished sending the binary output, so I added a newline character where the END tag currently is. That had the unfortunate result of sending will more then triple the actual file size to the program. Thus I came up with the END tag you see here.

My question is: "Why did that stuff happen?" Am I missing some fundamental concept?

Thanks,
Boots

Replies are listed 'Best First'.
Re: client server file transfer
by repson (Chaplain) on Jun 23, 2001 at 14:24 UTC
    If you have full control over the protocol and both client and server implementation then I would consider a different method. It would be possible to continue using a end tag, but it can be difficult to do that correctly and reliably without fiddling with various selects and reading methods and hoping that piece of data won't appear in the file.

    Alternativly I would do what some protocols do and use a message content length (in exact bytes) so that the reciver knows exactly how much binary data to read, after which normal operation can resume.

    For this you could just add a line to the sender to like print OUT "Size: " . (-s $file) . "\n"; and the reciver can parse that line of data and use one or a series of read statements to recieve exactly that much data.

      I agree with you about the tags, I was still in the "hope that the problem doesn't show up" phase. However, that little trick (-s) to send the filesize solves this problem perfectly.

      Thanks for your help.
      Boots
Re: client server file transfer
by dimmesdale (Friar) on Jun 23, 2001 at 01:04 UTC
    Yes. Well, at least I think. From reading the Perl Cookbook's section on sockets and client/server connections, what I gather is that the way you are implementing it, there is no way for the one to tell the other is done writing; and no way for the other to tell the one is done reading. This causes a server hang. There's probably some info on this if you search this site(or Yahoo, or another search engine).

    update: Right problem, wrong solution. I was thinking of fork's; sorry. Though some good suggestions have already been offered, so I'll just leave it at that.

      I don't understand how that would work at all...

      The SendFile function has not access to OUT. (OUT is on the client program which is on an entirely different computer than then the server)

      Regardless, I tried that and it does not seem to work, are you sure that the book did not mean to send a "close OUT" variable after the file?

      Although thanks for the input, the concept of not being sure whether they had finished writing back and forth was what I had suspected. I just want to get a concrete verification
      Boots
        A socket is just a stream. How do you know when one rain's water ends and another begins in a river?

        You're going to have to write some kind of mark on the stream so the receiving end knows when things are done, or send and out-of-band message. One kind of mark is to simply shut down the socket on the sending end (close $to in Sendfile). The client can sense this. If you want to be absolutely sure the whole thing got over there, make the last few (fixed length) bytes a checksum of some kind.

        If you insist on keeping the socket open, then you're going to have to send a marker just like you are now. Just make sure it's NEVER going to happen in mid-stream though. You could mimic what multipart MIME does and send a long, unique string at the beginning of the stream and another at the end. (Heck, doing that lets you set $/ to the closing string. Some nice code bumming if you're up for it.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://90861]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (7)
As of 2024-04-25 15:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found