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

hi monks, i have the following code that works 100% basically i have a open port and it listens for connections then once a connection is established it saves all the data to a log file, the problem is the connection will stay open for 12 hrs + and keep sending data, so how can i save the second and third etc connection's data, i might hve up to 50 connections at a time here is my existing code
#!/usr/bin/perl -w my $socket; my $port = 7777; my $host; use IO::Socket; use Sys::Hostname; $host = hostname(); my $sock = new IO::Socket::INET( LocalHost => $host, LocalPort => $port, Proto => 'tcp', Listen => 5, Reuse => 1, ); if (!$sock){ die "no socket :$!"; } my($new_sock, $c_addr, $buf); #open($log, '>>', 'log.txt') || die "Couldn't open log.txt: $!"; 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] "; while (defined ($buf = <$new_sock>)) { open ($log, '>>','log.txt') || die "Couldn't open log.txt: $!"; print $buf; print $log $buf; close $log; } }
and here is an example of data recieved
$MENQ,run,l1, 0E13,,839469 $GPRMC,152457.001,A,2232.3347,N,11401.2021,E,0.2,017.0,180507,,,A*66 $MENQ,run,c9, 0E13,,839469 $GPRMC,152612.001,A,2232.3722,N,11401.2106,E,0.1,194.9,180507,,,A*66 $MENQ,run,f9, 0E13,,839469 $GPRMC,152642.001,A,2232.3245,N,11401.1969,E,0.1,198.0,180507,,,A*60 $MENQ,run,f9, 0E13,,839469 $GPRMC,152642.001,A,2232.3245,N,11401.1969,E,0.1,198.0,180507,,,A*60
thanks K-

Replies are listed 'Best First'.
Re: multiple connections
by clinton (Priest) on May 18, 2007 at 15:54 UTC
    Sorry xarex, what is your question:

    • How do you allow multiple connections at the same time, or
    • having multiple connections, how do you write the data from all of them to the same (different?) log file(s)?

    I notice that you are opening and closing your log file all the time, but there is no need to do so (as long as each line is less than about 2kB). The file buffer handles that, and allows multiple processes to append to the same open filehandle.

    hth

    Clint

      clint, the problem is, at the moment i am writing to a single file, it may be better to write different file for different connections, because i have a php script that processes the data, how can i achieve this? thank k-
        Writing to a single file shouldn't be a problem. Do you mean that the problem is that you want to separate the data from the different connections? If so, you could prepend an ID or something to the beginning of the line to distinguish between connections:

        print $log "$connection : $buf";

        or you could use the same logic to open different files :

        open ($log,'>>',"log_$connection.txt") or die $!;

        ...where $connection is a variable that increments for every new connection that opens, or is based on the IP address, or whatever is meaningful to you.

        Have I understood the problem correctly?

        hth

        clint

Re: multiple connections
by BrowserUk (Patriarch) on May 18, 2007 at 16:52 UTC

    Add the following three lines to your current program, and it will allow it to maintain as many concurrent connections as you have memory to support. That should be at least 100.

    If that's too limiting, there are some simple things that can be done to extend that to 1000's. And they say threads are hard :)

    Your code is on need of a little cleaning up, like opening the log file outside of the read loop, and closing it once the connection drops, but in essence it should just work.

    #!/usr/bin/perl -w use threads; ######################## Added my $socket; my $port = 7777; my $host; use IO::Socket; use Sys::Hostname; $host = hostname(); my $sock = new IO::Socket::INET( LocalHost => $host, LocalPort => $port, Proto => 'tcp', Listen => 5, Reuse => 1, ); if (!$sock){ die "no socket :$!"; } my($new_sock, $c_addr, $buf); #open($log, '>>', 'log.txt') || die "Couldn't open log.txt: $!"; while (($new_sock, $c_addr) = $sock->accept()) { async { ################## Added 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] "; while (defined ($buf = <$new_sock>)) { open ($log, '>>','log.txt') || die "Couldn't open log.txt: $!"; print $buf; print $log $buf; close $log; } } ########################## Added }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Hi BrowserUK, Could you give me some tips on how to clean up the code, If i could extend it to 1000's of connections that would be fantastic, Also would it somehow be possible to create different logs based on the ip? K-
        Could you give me some tips on how to clean up the code, If i could extend it to 1000's of connections that would be fantastic, Also would it somehow be possible to create different logs based on the ip?

        Hmm. Sure, but there are some worrying signs in your questions.

        Cleaning up the piece of code that you posted and making it write a separate log file for each in-bound ip is ... well, frankly easy. That is, it is a pretty simple task and anyone with a small amount of programming experience would be able to see how to do this themselves.

        If you cannot see how to do that, it makes me nervous about trying to help you further as it tends to indicate that you are not looking for someone to help you, but rather looking for some to do this for you. This place is a self-help community, not a 'bespoke software for free' shop.

        Here's the thing. The greatest tip for writing good multitasking code is

        1. Make your code do everything you need it to do for one connection first.
        2. Then make it do the right thing for two serial connections.
        3. Then, when you are happy with that, make it do it for two concurrent connections.
        4. Once you have that working, extending it to do it for more, up to the limitations of your hardware is usually relatively simple.

        So, if you really just want help, not someone to write this for you, I suggest that you try and extend your original code to handle step 2 above.

        You say "that works 100% basically i have a open port and it listens for connections then once a connection is established it saves all the data to a log file, ...".

        As it is, each time you have something to write to a log file, you are opening that log file using the following line:

        open ($log, '>>','log.txt') || die "Couldn't open log.txt: $!";

        Now, that is obviously going to put all the data into a single file.

        You also have a variable, my $client_ipnum = inet_ntoa($c_ip); which from the name seems to indicate that it is the ip address of the connecting client.

        How do you think that you could modify your original program to write the input from different ips to different log files?


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: multiple connections
by bennymack (Pilgrim) on May 18, 2007 at 16:31 UTC

    It seems like you'll want to fork a process for every socket connection that comes in. This is a little slower than pre-forking processes but from your example, performance doesn't seem to be an issue yet. How about this code:

    #!/usr/bin/perl -w use strict; use warnings; my $port = 7777; my $host; use POSIX; use IO::Socket; my $sock = new IO::Socket::INET( Reuse => 1, Listen => 5, Proto => 'tcp', LocalAddr => "127.0.0.1:$port", ) || die "no socket :$!"; POSIX::setsid; my($new_sock, $c_addr, $buf); open( my $log, '>>', 'log.txt') || die "Couldn't open log.txt: $!"; 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"; # New process handles data till client disconnects. if( not fork ) { while (defined ($buf = <$new_sock>)) { print "$$ $buf"; print $log "$$ $buf"; } warn "$$ exiting: $client_host [$client_ipnum]\n"; exit 0; } } END { kill 'TERM', -$$; }

      One thing about that example I posted is that the log file writes are suffering from buffering. So you'll either want to turn off buffering or use syswrite.