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

Fellow monks,

after having searched the web for hours, trying to find an existing solution to my problem, I am now requesting your help.

Problem:
I am currently (trying) to write a client-server pair, that will be used to edit a file on a remote computer.

Because I can't be sure that either machine is constantly available, I want to monitor the server-client constantly. If the connection drops, a reconnect is initiated. I.e. I have the client constantly running, and only start the server app, when I need to change something.

After connecting, the communication starts from the server-side by requesting the current content of the file. That way I could easily (sort of) implement the monitoring of the client-side connection:

sub ReadData{ my $recv_data; my $send_data; my $command; my $working_data; while ($endEx eq "false"){ if ($connected eq "true") { $socket->recv($recv_data,1024); if ($recv_data ne "") { # make sure, we can read from the + socket, if ($debug_log eq "on") { &WriteLog("Message received: $recv_data"); } $command = substr($recv_data , 0 , 3); if ($command eq "GET"){ $send_data = "OLD:" . &GetCurrent; } elsif ($command eq "PUT"){ $working_data = substr($recv_data , 4); &SetNew($working_data); $send_data = "NEW:" . &GetCurrent; } #elsif ($command eq "SET"){ #will be used in t +he future for setting startstop handling #} if ($command eq "GET" || $command eq "PUT"){ $socket->send($send_data); if ($debug_log eq "on") { &WriteLog("Message sent: $send_data"); } } } else{ $connected = "false"; # if not, reset socket an +d reconnect Win32::GUI::NotifyIcon::Change( $ni, -icon => $icon_no, -tip => "connecting...", ); } usleep($reconnecttime); } else { &WriteLog("Connection lost"); close $socket; $socket=""; &ConnectServer(); } } }
If the client can't read from the socket any longer, it is asumed, that the connection is broken. I close the socket and reopen a new one. Works fine.
The server is the one that really gives me a hard time.
The establishment of the connection is very easy. I just can't get the monitoring to work. I tried all kinds of aproaches:
  1. Using the same way as for the client, won't work, as I am not reading from the port, but rather waiting for something to send to the client
  2. I tried multithteading, but couldn't get two threads to a)read, i.e monitor and b) prepare to write to the same socket
Does anybody have experience on how to implement something like that?
I really appreciate your help,
Thanks,
Sven

Replies are listed 'Best First'.
Re: TCP server, that realizes connection loss
by mykl (Monk) on Jul 14, 2009 at 14:52 UTC
    I had this problem (wanting to detect when the remote PC had closed the connection) and I eventually found by accident that when the connection was closed remotely, a select() call would indicate that the remote PC had sent data, and zero bytes of data were returned by recv(). This doesn't happen in any other situation, so I successfully used this to detect a disconnection.

    I don't see this documented in perlfunc, so I'd be interested in know how robust and portable this is. (I am using perl 5.8.7 in Win32) In case it is of help, here is a small library I wrote using TCP connections to transfer YAML-serialised data for basic RPC.

    (criticism gratefully received)
Re: TCP server, that realizes connection loss
by jethro (Monsignor) on Jul 14, 2009 at 14:10 UTC

    Wouldn't a UDP connection make more sense for editing a file? The server is easier to program because there is no connection that can break. The traffic is more suited to UPD because it is always a single keystroke between loooong pauses (at least for the computer)

    UPDATE: Ah, I forgot that the changes have to be sent back to the client for display, which works better in a TCP connection.

Re: TCP server, that realizes connection loss
by Anonymous Monk on Jul 14, 2009 at 19:33 UTC
      Hi,

      select kinda does the trick. I can now monitor the server. If the client disconnects, it is captured and the thread running the read/write is killed.

      now I have another problem. Even though a new socket seems to be opened after reconnect, I can only write to it, when I have the print"$pid \n"; before cancelling the thread. If i comment that out, it just prints out:

      SEND( TYPE q or Q to Quit): RECIEVED: SEND( TYPE q or Q to Quit): RECIEVED: SEND( TYPE q or Q to Quit):Use of uninitialized value $send_data in sc +alar chop at select_server.pl line 46, <STDIN> line 5. RECIEVED: SEND( TYPE q or Q to Quit):Terminating on signal SIGINT(2)

      The error above shows up when hitting CTRL-C

      What am I missing?
      use IO::Select; use IO::Socket(); use threads; use threads::shared; use Thread::Cancel ; use strict; use warnings; my $Thread; my $lsn = new IO::Socket::INET(Listen => 1, LocalPort => 5000); my $sel = new IO::Select( $lsn ); my $tid : shared; while(my @ready = $sel->can_read) { foreach my $fh (@ready) { if($fh == $lsn) { # Create a new socket my $new = $lsn->accept; $sel->add($new); my $Thread = threads->create( "echoserver", $new); #s +tart the echo-server as a seperate thread #$Thread->detach(); } else { # Process socket # Maybe we have finished with the socket print "$tid\n"; my $Thread = threads->object($tid); #get handle of cu +rrent echo-server thread $sel->remove($fh); $fh->close; threads->cancel($Thread); #kill the thread. + a new one will get started when we reconnect } } } sub echoserver { #Clienthandle my $session = shift; my $own_thread; # get our own thread-id and store it, so we can kill the thread fr +om outside $own_thread=threads->self(); $tid = threads->tid($own_thread); lock($tid); $session->autoflush(1); #make sure the socket get read right aw +ay while (1) {print "\n SEND( TYPE q or Q to Quit):"; my $recieved_data; my $send_data = <STDIN>; STDIN->autoflush(1); chop($send_data); $session->send($send_data); $session->recv($recieved_data,1024);print "\n RECIEVED: $recie +ved_data";} }

      Thanks,

      Sven
Re: TCP server, that realizes connection loss
by sven (Initiate) on Sep 02, 2009 at 08:42 UTC
    Done,
    Thanks for all your help.

    I now use a queue to pause the thread.

    Sven