Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

IO Socket buffer flushing

by baldeep8 (Initiate)
on Jan 29, 2010 at 10:09 UTC ( [id://820327]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks, I have created a simple client and server program which will just consecutively write and read data from a single socket connection. I have enabled non blocking socket using ioctl to ensure that in case i dont get anything to read from the socket then sysread will not wait there indefinitely. If ioctl is disabled then i am able to send and receive the appropriate data on both the client and server. With ioctl enabled i am not able to receive the data on the client side consistently though the sysread no longer blocks when there is no data to read. I have done 2 syswrites and 2 sysreads on both the client and server sides. On the client side i find that sometimes i get the data of the first sysread operation when i do the second sysread. The same problem happens with sysread on the server side as well. It looks like the socket buffer is not getting flushed. I have tried a number of methods to flush the buffer including autoflush, flush, $| and print LOG "". In all cases still i find that the buffer does not get properly flushed and that the problem persists. I am new to socket programming in Perl. Please help.

Client code :
use IO::Socket; #To flush the buffer print statements $| = 1; my $sock = new IO::Socket::INET( PeerAddr => 'localhost', PeerPort => +7890, Proto => 'tcp'); if ($sock) { print "A tcp socket on localhost connected to 7890\n"; } else { die "Error: $!"; } $nonblocking = 1; ioctl($sock, 0x8004667e, \$nonblocking); $buf = "1234567890"; syswrite($sock, $buf, 10); sysread($sock, $buf, 10); $sock->flush; print "Bytes 10 = $buf\n"; syswrite($sock, "1", 1); print "\n"; $sock->flush; sysread($sock, $buf, 2); print "Bytes 2 = $buf\n";
Server code:
use IO::Socket; $| = 1; my $sock = new IO::Socket::INET( LocalHost => "localhost", LocalPort => 7890, Proto => "tcp", Listen => SOMAXCONN, Reuse => 1, Timeout => 20 ); if ($sock) { print "A $Proto socket created on $LocalHost listening on $LocalPor +t\n"; } else { die "Error - no listening socket created : $!"; } $flush = 1; while (($new_sock,$c_addr) = $sock->accept()) { my ($client_port, $c_ip) = sockaddr_in($c_addr); my $client_ipnum = inet_ntoa($c_ip); my $client_host =gethostbyaddr($c_ip, AF_INET); print "Got a connection from: $client_host"," [$client_ipnum] \n"; print "Created new socket for reading or writing data to Client\n"; $nonblocking = 1; ioctl($new_sock, 0x8004667e, \$nonblocking); sysread($new_sock, $buf, 10); print "$buf\n"; syswrite($new_sock, $buf, 10); $new_sock->flush; sysread($new_sock, $buf, 1); print "$buf\n"; $new_sock->flush; syswrite($new_sock, "12", 2); }

Replies are listed 'Best First'.
Re: IO Socket buffer flushing
by desemondo (Hermit) on Jan 29, 2010 at 11:22 UTC
    1. use strict; use warnings; They are your friends, and will save you many many many hours of troubleshooting and head scratching. They aren't perfect, you'll still have to do some work, but certainly a lot less...

    2. Keep your code layout tidy paying particular attention to indentation. It may feel awkward, even unnessesary at first, but it'll get easier and easier until you find yourself writing well layed out code without even thinking about it... In short, well layed out code will make your life easier.

    3. Well done for posting a short example of your code that demonstrates the problem. In this particuar case it would also be worth posting an example of the output you are seeing. Often its easy to just stick __END__ on the end of your code post and paste your output below it.

    4. I tried your code (modified slightly to conform to strict rules) and it seems to work fine. No weird buffering issues are apparent... If the revised code below don't solve the problem your having, please clarify with further details and what output your seeing. Cheers!


    Your code slightly modified:

      Just playing around with sockets (cause its fun) I worked up the following code. Its not better realy, just different, and doesn't count on send a specific amount of info each time, intead it uses ->say and ->getline to send whole lines of data. I had to use chomp a couple times and i'm wondering if that could be avoided, but here you have it.

      #!/usr/bin/perl #server use strict; use warnings; use IO::Socket; $| = 1; my $nonblocking = 1; my $sock = new IO::Socket::INET( LocalHost => "localhost", LocalPort => 7890, Proto => "tcp", Listen => SOMAXCONN, Reuse => 1, Timeout => 20, ); if ($sock) { print "A socket created on LocalHost listening on LocalPort\n"; } else { die "Error - no listening socket created : $!"; } my $flush = 1; while (my ($new_sock,$c_addr) = $sock->accept()) { my ($client_port, $c_ip) = sockaddr_in($c_addr); my $client_ipnum = inet_ntoa($c_ip); my $client_host =gethostbyaddr($c_ip, AF_INET); print "Got a connection from: $client_host"," [$client_ipnum] \n"; print "Created new socket for reading or writing data to Client\n" +; $new_sock->blocking(1); my $buf; $buf = $new_sock->getline(); chomp($buf); print "Recieved 1 '$buf'\n"; $new_sock->say($buf); $buf = $new_sock->getline(); chomp($buf); print "Recieved 2 '$buf'\n"; $new_sock->say("12"); }
      #!/usr/bin/perl #client use strict; use warnings; use IO::Socket; #To flush the buffer print statements $| = 1; my $sock = new IO::Socket::INET( PeerAddr => 'localhost', PeerPort => +7890, Proto => 'tcp'); if ($sock) { print "A tcp socket on localhost connected to 7890\n"; } else { die "Error: $!"; } my $buf = "1234567890"; $sock->say($buf); print "Sent '$buf'\n"; $buf = $sock->getline(); chomp($buf); print "Bytes 10 = '$buf'\n"; $sock->say("1"); $buf = $sock->getline(); chomp($buf); print "Bytes 2 = '$buf'\n";

      ___________
      Eric Hodges

        TCP provides a stream of bytes. You need to do your own record delimiting. readline works because it waits until a delimiter is found.

        If you need a sysread version (perhaps so you can use select), see Re^3: Can a socket listen for multiple hosts? for an example.

Re: IO Socket buffer flushing
by ikegami (Patriarch) on Jan 29, 2010 at 16:06 UTC
    Some simple stuff first:
    • IO::Socket sets the socket to autoflush, so all your ->flush() are useless.

    • $nonblocking = 1; ioctl($sock, 0x8004667e, \$nonblocking);

      is an awful way of writing

      $sock->blocking(0);

    If I understand correctly, your problem is that two writes in quick succession end up being picked up by the second read of two reads in quick succession. If you consider that a problem, you're doing something majorly wrong. Maybe we can help you fix your actual problem if you tell us about it.

      Yes I am also encountering the issue where two successive writes are picked up by second read. Please let me know how to handle this. My scenario is this one. I have multiple files whose data has to be sent via a socket and on the same connection the response data has to be picked up. I am able to get responses for the first two files. But for third file 40 % of the response data is being written to one output file and the rest to another file. There is no consistency in the recv of response and then writing the data to the output files. any help is appreciated. Thanks

        You don't show us any code. You don't show us how you're writing the files. If you use a delimiter between the two files, it's far more likely that there is an error in your (non-socket) program logic that mixes up the content of the files. Also, I wouldn't bet on a single read corresponding to a single write on the other end.

        I am also encountering the issue where two successive writes are picked up by second read.

        Not surprisingly, the same answer applies to you.

Re: IO Socket buffer flushing
by zentara (Cardinal) on Jan 29, 2010 at 13:24 UTC
    I havn't run your code, but it looks like you are using very low level socket code, and that involves alot of handshaking, which you may need improving upon. Probably, you are getting into a deadlock condition, where both sides of the socket connection are waiting to send or receive.

    One quick thing to try is put newlines into all syswrites, like

    # syswrite($new_sock, $buf, 10); syswrite( $new_sock , "$buf\n\n", 10);
    Another thing to try is using select ( or IO::Select) on the socket filehandles. That way you can do the read in a $select->can_read loop, which will keep the deadlock from occurring. Google for numerous Perl socket examples, like "perl socket IO::Select".

    Also see the perl socket examples a basic socket tutorial


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (2)
As of 2024-10-07 17:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    The PerlMonks site front end has:





    Results (44 votes). Check out past polls.

    Notices?
    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.