in reply to Non closing sockets - threads

Hm. There is a lot of very dubious stuff going on in your code. At a guess, I think you are not using strict, and only adding it to avoid critism when posting. This is a false economy--actually it's no economy at all. Please do not be upset by what follows, it is intended to help you, and to reduce the confusion in your code to the point where the problem you are having becomes soluable.

To that dubious stuff:

  1. Why are you using our instead of my.

    There are very specific times when our is useful, but unless you know that you need it, you probably don't and should not use it. So, switch all those ours to mys.

  2. Why are you declaring variables miles away from there point of use? Eg. $k $n $m $p.

    One of those isn't even used ($m), and the rest are used a loop counters. $p is declared in the main thread and used in the thread procedure.

    Switch them to my and declare them at their point of use:

    for( my $p = 0; ...)

    I might come back and deal with the C-style for loops later.

    Likewise, $dup, $SyncHeader, $SyncID, $UnitID, $sel_cli2 can all be declared at the point of use; and $buf2 & $ref can deleted entirely.

  3. Threads get copies of non-shared variables that exist when they are spawned!

    So, the copy of my @Mdevices; that the main thread has will never see changes made by the thread procedure. That seems to be a part of your mechanism for cleaning up socket handles, and it would have to be shared for that to stand a chance of working. But things are still confused enough that I'm not sure it would work even if it was shared. So I'll come back to that.

  4. Your thread proc (reformatted a litte to prevent wrapping) contains lots of problems:

    Please see the embedded comments!

    sub processit { my( $lclient, $lfileno, $lpeer ) = @_; my $sel_cli_id = $num_of_client; ######### This is never used. if( $lclient->connected ){ while( <$lclient> ) { ### $peerC si shared by cloning ### It sole purpose appears to be to allow you to share ### a single thread proc by two different types of thread. if( $peerC eq '127.0.0.1' ) { my(@comm) = split(/:/, $_); ### These 3 lines is bett +er as one my $sel_clix = $comm[0]; my $commandC = $comm[1]; my $sel_cli2; ### This will only see a copy of whatever is in @MDevi +ces ### when this thread is spawned. ### And none of teh subsequent changes! ### You'd be far better avoiding a linear search by us +ing a hash for( my $p=0; $p <= $#Mdevices; $p++ ) { if( $Mdevices[ $p ][ 0 ] eq $sel_clix ) { $sel_cli2 = $Mdevices[ $p ][ 1 ]; } } ### A loop that iterates a single thing?? foreach my $fn ( $sel_cli2 ) { if( $sel_cli2 ) { open my $fh, ">&=$fn" or warn $! and die; print "\n $lclient $sel_cli2 Command ", "sent to: $sel_clix Command: $commandC"; print $fh "$commandC\n" } else { print "\n $lclient Device: $sel_clix", "NOT avaiable to receive command\n"; } } } else { print "COMMAND RESPONCE: $_"; } } } close( $lclient ); #### Modifying a shared variable without locking it #### Will cause problems. @clients = grep{ $_ !~ $lfileno } @clients; }

    What makes th above code so confusing is that you are trying to use one thread procedure to do double service for two different types of thread: The gprs clients and the localhost command entry client.

    It would be far better to have two thread procedures and start the appropriate one depending upon where the in-bound connection comes from. Each of the thread procs would be simpler and need to share less information.

  5. The main thread.

    Most of the complexity of your main thread comes from using an array @MDevices, and having to iterate it (twice!) to first find, and then delete things, when if you used a hash that could be done with one or two single statements.

The problem you cite: "And there is my problem: Old threads remains connected to the server and I need to get rid of them!" is caused by having multiple handles (one in the main thread and another in the client threads),connected to each client, but only closing one of them.

You are (I think) attempting to deal with this using @MDevices, but as it is not shared, the changes are not being propogated.

This post is just by way of explanation of what is wrong. It'll take a while to work out (and test given I don't have any gprs devices) a solution, but I'll try to post something later today or tomorrow.


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.
"Too many [] have been sedated by an oppressive environment of political correctness and risk aversion."

Replies are listed 'Best First'.
Re^2: Non closing sockets - threads
by igor1212 (Novice) on Dec 15, 2008 at 17:39 UTC
    Hello BrowserUk

    Thanks for your help, I'm not upset at all, actually I'm very happy for your help.
    About strict, it was really used, I would never put strict just to avoid criticism.

    Can't wait to see your next post.

    Best regards
    Igor

      A few questions:

      1. You say above that Old threads remains connected to the server and I need to get rid of them!

        Do you mean: old threads within the server persist (continue to run)? For a while? Forever? Or

        Do you mean old connections persist? For a while? Forever?

        Either way, how long have you waited to see if they (whichever) will clean themselves up?

      2. Does your current code work? Ie:
      3. Do your commands get sent to the gprs devices?
      4. Does that happen immediately, or with some delay?
      5. From a previous thread, I recall that these gprs devices regularly send data (GPS coordinates?) to the server. What frequency does that happen?

      This code should be essentially similar to your existing code (and may even display the same problems; though I'm hoping not).

      Give it a try and let me know how you get on. (Error handling is perfunctory!).

      #! perl -slw use strict; use IO::Socket; use threads; use threads::shared; $|++; print "$$ Server started\n"; my %devices :shared; my $server = new IO::Socket::INET( Timeout => 500, Proto => "tcp", LocalPort => 7777, Reuse => 1, Listen => 5 ); while( my $client = $server->accept ) { next unless defined $client; my $peerhost = $client->peerhost; read $client, my $packet, 8 or warn "Couldn't read sync packet: $!" and close $client and next; ## No use for syncHeader or syncID so discard them my $unitId = unpack 'x[vv]V', $packet; ## start the appropriate type of client thread threads->create( $peerhost == '127.0.0.1' ? \&cmdClient : \&gprsClient, $unitId == 437918234 ? \&cmdClient : \&gprsClient, $client, $unitId )->detach; } sub cmdClient { my( $client, $unitId ) = @_; my $fileno = fileno $client; warn "Command Client running on $fileno\n"; while( <$client> ) { my( $unitId, $cmd ) = split ':'; warn "Got command '$cmd' for [$unitId]\n"; ## Get the fileno for the required unitId my $gprsFno = do{ lock %devices; unless( exists $devices{ $unitId } and defined $devices{ $unitId } ) { warn "No device with unitId '$unitId' currently conne +cted" and next }; $devices{ $unitId } }; ## dup it for output (NOTE: '>&' NOT '>=&') open my $fh, '>&' . $gprsFno or warn "Failed to dup( $gprsFno ) [$unitId]: $!" and next; ## Send the command (Won't happen (on my system) ## until that unit sends us something!!!) print $fh $cmd or warn "Failed to write command '$cmd' to [$unitId]" and close $fh and next; warn "Sent '$cmd' to [$unitId]"; close $fh; } close $client; warn "cmdClient [$unitId] disconnected\n"; } sub gprsClient { my( $client, $unitId ) = @_; my $fileno = fileno $client; ## Add device to shared lookup table indexed by $unitId { lock %devices; $devices{ $unitId } = $fileno; } warn "gprsClient [$unitId] running on $fileno\n"; ## Simply echo all input from the gprs device print "$unitId: Command response: $_" while <$client>; ## When the connection terminates, remove it from the lookup lock %devices; delete $devices{ $unitId }; close $client; warn "gprsClient [$unitId] disconnected\n"; }

      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.
        Hello BrowserUk,

        Really thanks for your help!

        I'm getting this error with your code: thread failed to start: Not a GLOB reference at tcpserver4.pl line 43 (and 83).

        Old threads within the server persists for at least 10 hours
        I've checked with netstat -apn | grep 8000
        After tcp server is restarted they disappear

        My code except this, works as it should and it immediately sends the command to the Selected client without delay.
        I do include with client command data also the UnitID to whom it must send the command.
        Gprs device sends gps data at minimum 10 second interval and sends sync data at a definable interval (currently at about 5 minutes)

        Regards, Igor