Newbie needs help with IO::Socket (maybe IO::Select)

Discussion in 'Perl Misc' started by Boerni, Feb 29, 2008.

  1. Boerni

    Boerni Guest

    Hi all

    I'm playing around with IO::Socket and try to build a small Client/Server
    App.

    The Serverpart is working as it's supposed to, but I have some problems with
    the Clientpart.

    So I have a couple of Questions:
    1. How can a Client react to a Servermessage (Server prints to Socket), when
    it's not expecting a Servermessage (For example another client connects to
    the server and kicks the first one or the server is beeing shutdown). If I
    use the IO::Select approach like in the Serverpart, the Client is blocked
    and the User can't interact with it anymore.

    2. Why does $socket->connected still return the peer-address, eventhough the
    server is shutdown?

    I'm sure there is a solution for this (probably by using IO::Select), but I
    can't find any examples of Clientcode using this.

    Here's what I have (simplified... the "real" Client is using Tk):

    ------ Star Serverpart -------
    #!perl -w
    use strict;
    use IO::Socket;
    use IO::Select;
    use Data::Dumper;

    our $socket;
    my %users;
    my $port = 9901;

    #Socket erstellen
    my $main_socket = IO::Socket::INET->new(LocalPort => $port,
    Type => SOCK_STREAM,
    Reuse => 1,
    Listen => 1) or die "Kann kein
    TCP-Server an Port $port sein: @!\n";

    my $lesbar = new IO::Select();
    $lesbar->add($main_socket);

    #Pruefen ob ein Client connectet
    while(1) {
    my ($neu_lesbare) = IO::Select->select($lesbar, undef, undef, undef);

    foreach $socket(@{$neu_lesbare}) {
    if ($socket == $main_socket) {
    #Neue Verbindung kommt rein...
    my $neues_socket = $socket->accept();
    $lesbar->add($neues_socket);
    }
    else {
    # Hier drin passiert Zeug mit dem Client
    my $buf = <$socket>;
    if ($buf) {
    chomp($buf);
    my @args = split(',', $buf);
    if ($args[0] eq "AUTH") {
    auth($args[1], $args[2]);
    }
    else {
    print "Nicht unterstuetzter Command\n";
    }
    }
    else {
    # print "Client hat Socket geschlossen\n";
    $lesbar->remove($socket);
    close ($socket);

    foreach (keys %users) {
    if ($socket eq $users{$_}{socket}) {
    print localtime().": User [$_] disconnected\n";
    delete $users{$_};
    }
    }
    }
    }
    }
    }

    #---- Subroutinen ---#
    sub write2socket {
    my $socket = shift;
    my $text = shift;
    my $length = sprintf("%04d", length($text));
    my $newstring = $length.$text;
    print $socket "$newstring";
    }

    sub auth {
    my $usr = shift;
    my $pass = shift;

    #Na wer ists denn...
    my $iaddr = inet_ntoa($socket->peeraddr());
    my $rem_host = gethostbyaddr($socket->peeraddr(), AF_INET);
    my $rem_port = $socket->peerport();
    print localtime().": Neue Verbindung von Host: $rem_host
    [$iaddr:$rem_port]\n";

    if (($usr eq "boerni") or ($usr eq "admin")) {
    if ($users{$usr}) {
    write2socket ($socket,"User $usr ist bereits verbunden
    ($users{$usr}{host}:$users{$usr}{port})");
    }
    else {
    write2socket ($socket,"Verbindung mit User $usr akzeptiert");
    $users{$usr}{socket} = $socket;
    $users{$usr}{host} = $rem_host;
    $users{$usr}{port} = $rem_port;
    }
    }
    else {
    write2socket ($socket, "Verbindung abgelehnt");
    $lesbar->remove($socket);
    $socket->shutdown(2);
    close ($socket);
    }

    #print Dumper \%users;
    #print Dumper \%sockets;

    foreach (keys %users) {
    my $handle = $users{$_}{socket};
    write2socket($handle, "User $usr hat sich verbunden");
    }
    }
    ------ End Serverpart -------

    ------ Star Clientpart -------
    #!perl -w
    use strict;
    use IO::Socket;
    use IO::Select;

    my $socket;
    my $usr = "admin";
    my $pw = "pass";
    my $remote_host = 'localhost';
    my $remote_port = 9901;

    my $svrmsg;

    my $lesbar = new IO::Select();
    $lesbar->add($socket);

    while (<>) {
    chomp;
    if ($_ eq "connect") {
    connect2server();
    }
    elsif ($_ eq "man_move") {
    man_move("up");
    }
    elsif ($_ eq "check") {
    check_status();
    }
    else {
    print "Nicht unterstuetzt\n";
    }
    }

    #---- Subroutinen ----#
    sub connect2server {
    #---- Socket aufbauen ----#
    $socket = IO::Socket::INET->new(PeerAddr => $remote_host,
    PeerPort => $remote_port,
    Proto => "tcp",
    Type => SOCK_STREAM)
    or die "Konnte Verbindung zu $remote_host:$remote_port nicht herstellen:
    @!\n";

    print $socket "AUTH,$usr,$pw\n";

    $svrmsg = read_from_sock($socket);
    print "FROM SERVER: [$svrmsg]\n";

    }

    sub man_move {
    return if ! $socket;
    my $direction = shift;
    print $socket "man_move,$direction\n";

    $svrmsg = read_from_sock($socket);
    print "FROM SERVER: $svrmsg\n";
    }

    sub read_from_sock {
    my $socket = shift;
    my $length = 0;
    my $data;

    $socket->read($length, 4);
    $socket->read($data,$length);

    return $data;
    }

    sub check_status {
    if (! $socket) {
    print "CHECK: No socket\n";
    }
    elsif ($socket->connected()) {
    print "CHECK: Connected with ".inet_ntoa($socket->peeraddr())."\n";
    }
    else {
    print "CHECK: Not connected";
    }
    }
    ------ End Clientpart -------

    Thanks a lot
    Boerni, Feb 29, 2008
    #1
    1. Advertising

  2. Boerni

    Guest

    "Boerni" <> wrote:
    > Hi all
    >
    > I'm playing around with IO::Socket and try to build a small Client/Server
    > App.
    >
    > The Serverpart is working as it's supposed to, but I have some problems
    > with the Clientpart.
    >
    > So I have a couple of Questions:
    > 1. How can a Client react to a Servermessage (Server prints to Socket),
    > when it's not expecting a Servermessage (For example another client
    > connects to the server and kicks the first one or the server is beeing
    > shutdown).


    Why would another client connecting to the server kick off the first one?
    Wouldn't it be easier to fix this problem than to deal with the
    consequences?

    If the server has gone away, the client will realize this next time it
    tries to communicate with it. While I guess it might be nice to know
    immediately that the server has gone away, plenty of very good
    client-server apps don't detect server failure until the next time it tries
    to communicate. I'd need a very compelling reason not to follow this
    paradigm for my own clients.

    > If I use the IO::Select approach like in the Serverpart, the
    > Client is blocked and the User can't interact with it anymore.


    You need to add STDIN (or whatever you use to communicate with the User)
    into the IO::Select, too. This means you probably have to use sysread,
    rather than <>, to read from STDIN.

    .....

    > #Pruefen ob ein Client connectet
    > while(1) {
    > my ($neu_lesbare) = IO::Select->select($lesbar, undef, undef, undef);
    >
    > foreach $socket(@{$neu_lesbare}) {
    > if ($socket == $main_socket) {
    > #Neue Verbindung kommt rein...
    > my $neues_socket = $socket->accept();
    > $lesbar->add($neues_socket);
    > }
    > else {
    > # Hier drin passiert Zeug mit dem Client
    > my $buf = <$socket>;


    You are mixing buffered IO with select. This is rather dangerous. If
    the client can ever print two (or more) lines into $socket in quick
    succession, then this code might cause both of them to be read into the
    perl internal buffer, but only one of them to be returned from there
    into $buf. The next line just sits in the internal buffer, where it will
    not trigger IO::Select and hence not get read. The client won't send any
    more lines (which would trigger IO::Select) because it is waiting for a
    response to the last line it sent, and the server will never respond to
    that line because it doesn't know that it is there. Deadlock.

    I think IO::Select (or some subclass thereof) should return readable for
    any file handle that has data sitting in the perl internal buffer, in
    addition to the handles that have data (or eof) in the system's buffers. I
    haven't quite figured out how to implement that. All of those globs and
    glob refs and PerlIO layers and tied handles and scalar masquerading as
    handles are just too much for me.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    The costs of publication of this article were defrayed in part by the
    payment of page charges. This article must therefore be hereby marked
    advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
    this fact.
    , Feb 29, 2008
    #2
    1. Advertising

  3. Boerni

    Boerni Guest

    Thank you very much for your answer!

    > Why would another client connecting to the server kick off the first one?
    > If the server has gone away, the client will realize this next time it
    > tries to communicate with it. While I guess it might be nice to know
    > immediately that the server has gone away, plenty of very good
    > client-server apps don't detect server failure until the next time it
    > tries
    > to communicate. I'd need a very compelling reason not to follow this
    > paradigm for my own clients.


    I bought one of those USB-Missilelaunchers. So what I'm trying to do is,
    writing a client/server app, so everyone in my team can use it. But since
    I'm the one who bought it, I wan't to be able to kick anyone connected, when
    I connect :)
    But I guess it's ok, when they get the message the next time they try to
    send any command to the launcher.

    > You need to add STDIN (or whatever you use to communicate with the User)
    > into the IO::Select, too. This means you probably have to use sysread,
    > rather than <>, to read from STDIN.
    >
    > You are mixing buffered IO with select. This is rather dangerous. If
    > the client can ever print two (or more) lines into $socket in quick
    > succession, then this code might cause both of them to be read into the
    > perl internal buffer, but only one of them to be returned from there
    > into $buf. The next line just sits in the internal buffer, where it will
    > not trigger IO::Select and hence not get read. The client won't send any
    > more lines (which would trigger IO::Select) because it is waiting for a
    > response to the last line it sent, and the server will never respond to
    > that line because it doesn't know that it is there. Deadlock.
    >
    > I think IO::Select (or some subclass thereof) should return readable for
    > any file handle that has data sitting in the perl internal buffer, in
    > addition to the handles that have data (or eof) in the system's buffers.
    > I
    > haven't quite figured out how to implement that. All of those globs and
    > glob refs and PerlIO layers and tied handles and scalar masquerading as
    > handles are just too much for me.
    >
    > Xho


    Thanks... I'm going to read up on this...
    Boerni, Mar 1, 2008
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Guest
    Replies:
    5
    Views:
    602
  2. Ville Vainio
    Replies:
    11
    Views:
    588
    Hamish Lawson
    Aug 10, 2004
  3. Laszlo Nagy
    Replies:
    1
    Views:
    4,757
    Mark Wooding
    Jan 27, 2009
  4. Jean-Paul Calderone
    Replies:
    0
    Views:
    942
    Jean-Paul Calderone
    Jan 27, 2009
  5. Matt
    Replies:
    1
    Views:
    135
Loading...

Share This Page