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

I have this code right here, which i want to be able to read and write to telnet, right now it can read from telnet, but it cant write. Does anybody know how to do this?
#!/usr/bin/perl use warnings; use IO::Socket; $server = IO::Socket::INET->new ( LocalPort => 1337, Type => SOCK_STREAM, Reuse => 1, Listen => 5 ) or die "could not open port\n"; warn "server ready waiting for connections..... \n"; while ($client = $server->accept()) { unless (defined($child_pid = fork())) {die "can not fork\n"}; if ($child_pid) { warn "client connected\n"; while($line = <$client>){ print $line; } }else{ while($line = <>) { print $client $line; } } }

Replies are listed 'Best First'.
Re: writing to telnet?
by fokat (Deacon) on Mar 09, 2003 at 18:37 UTC

    I wrote these as guidelines. Please do not feel intimidated, it's just that I want to provide as much feedback as I can. By all means, please ask more questions if you've got them. A few thoughts:

    • Always, always, always use strict.

    • Indentation is very important. Use the Preview button to insure that the code looks like you intend to. Your code probably contained tabs and spaces, that are expanded differently, so take the time to tidy up the code, as I also had to do :) It is also very nice if you line your braces, as I show in my code below.

    • I am unsure as to the purpose of the fork() call in your original post. If it was to handle the two directions of data flow, it was unnecesary. If you were trying to support multiple, concurrent clients, you were fork()ing the wrong code.

    • When using fork(), always think about what will happen to children after they die or exit. Perl's default behavior is to collect their exit status for you, but you might want to explicitly wait for them in order to provide some reporting, statistics, etc. You might want to take a look at this snippet by me for a better example of an invocation of fork(). Also note that you must make sure that the child effectively terminates after doing its work. Otherwise, it might jump to the start of the loop again.

    • The close $server in the children as well as the close $client in the server are good ways to save on file descriptors.

    • On the same vein, note that the return value of fork() is always defined. Errors are signalled by returning -1.

    • It is always a good idea to ->autoflush(1) when doing this kind of things. This insures that data is not buffered for long at the server side.

    This is a version I adapted from your snippet and that incorporates what I say. It can read and write fine from as many clients as your machine supports.

    #!/usr/bin/perl use strict; use warnings; use IO::Socket; my $server = IO::Socket::INET->new ( LocalPort => 1337, Type => SOCK_STREAM, Reuse => 1, Listen => 5 ) or die "could not open port\n"; warn "server ready waiting for connections..... \n"; my $client; while ($client = $server->accept()) { my $pid; while (not defined ($pid = fork())) { sleep 5; } if ($pid) { close $client; # Only meaningful in the client } else { $client->autoflush(1); # Always a good idea close $server; &do_your_stuff(); } } sub do_your_stuff { warn "client connected to pid $$\n"; while(my $line = <$client>) { print "client> ", $line; print $client "pid $$ > ", $line; } exit 0; }

    Update: Thanks (and ++) to chromatic for correcting my confusion with Perl's fork() and C's. Looks like this mistake is going to live with me for a lot of time...

    Best regards

    -lem, but some call me fokat

      note that the return value of fork() is always defined. Errors are signalled by returning -1.

      Which version of Perl is this? I've just checked the documentation for 5.6.0, 5.8.0, and 5.5.3, and they all claim to return undef on failure.

      Wow, very neat, both the rules and the code.

      Only one small thing, autoflush is turned on by default, unless someone is using versions earlier than 1.18. The module coming with AS5.8.0 is version 1.26.
Re: writing to telnet?
by pg (Canon) on Mar 09, 2003 at 18:25 UTC
    I am not sure why you mentioned telnet here, but any way, your code just works fine. I tested it with this client:
    use IO::Socket::INET; use strict; my $socket = new IO::Socket::INET(Proto => "tcp", PeerAddr => "localho +st", PeerPort => 1337); while (1) { print $socket "1234\n"; my $msg = <$socket>; print $msg; sleep(5); }


    Your server can write to my client with no problem. Whatever I keyed in to STDIN on your server side, was printed on screen on my client side.
      i tried writing to your client with my server but it did not work, is there something i am missing here? thanks alot for the help.
        Should work. (I tried on win32, but don't think that matters in this case)

        The steps to test are:

        1. Open a dos window (shell), run your server, your script says "waiting for connections...."
        2. Open a second dos window, run my client. In the window running your server, your script says, "client connected", and also print out 1234, which is sent from my client
        3. Now in the window running your server, type "a msg from server", hit ENTER
        4. In the window running my client, you should see "a msg from server". Also in the window running your server, you should see another 1234, which is again sent from my client.
        gave a try ...
Re: writing to telnet?
by tachyon (Chancellor) on Mar 10, 2003 at 04:27 UTC

    Your basic problem is that you don't understand the telnet protocol. Short answer use Net::Telnet. Real answer Telnet protocol expects input from a terminal not a raw socket so you have to pretend to be a canonical terminal. RTFS of Net::Telnet together with the Telnet spec FWIW Telnet's default listening port is 23.

    As an aside you would be more secure to use SSH. Check out Net::SSH

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      tachyon, ++ for suggesting the use of Net::Telnet and Net::SSH, but...

      ...is it necessary to tell a person "you don't understand"?

      Please keep in mind that many of us are not native english speakers, and sometimes is really difficult to explain in English things that one could easily say in Italian, German, Hungarian... whatever! (Actually, I am in a big mess trying to explain in English what I am thinking in Italian). Sometimes it would be better to say "I don't understand what you are asking" or, like pg, "I am not sure why you mentioned telnet here".

      Don't get me wrong here: Ovid explained a simple "norm" before I did:

      A personal attack is where someone takes issue with me. However, if someone takes issue with the quality of my work, that's not a personal attack. There's quite a distinction between criticizing a person and criticizing what that person produces.

      Saying "You don't understand" doesn't criticize the code and, worse, one could think you criticize his ability to understand a concept; so there is a short way between "You don't understand" and "You are stupid"; and, of course, "You are stupid" would be personal attack.

      Ciao!
      --bronto


      The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
      --John M. Dlugosz

        I looked at your code, and the responses and surmised that (with the execption of 1 node) the problem at hand was that you (and a number or other monks) did not realise that you can't do a straight TCP socket and expect Telnet to just work. Thus I told you what the problem was, gave you the reason, links to the protocol (so you could understand it), and two solutions.

        No offense was intended.

        Sareste soltanto stupid se foste ancora programmantesi e domandantesi perchè non funziona. Invece avete fatto la domanda ed avete la soluzione.

        OK so my Perl is hopefully better than my Italian!

        ciao

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

        I am thinking in Italian

        And I thought thinking in ASM was messed up!

        Seriously though, I don't really think in any language, thoughts seem to be their own language. or "you can only think in code, the language converters work for the machines." Translating them to english is rather tedious.

Re: writing to telnet?
by Coruscate (Sexton) on Mar 10, 2003 at 02:48 UTC

    Another option is Net::Server (my specific example will use the Net::Server::PreForkSimple personality. This script will prefork 5 servers and therefore allow up to 5 users to interact with the server simultaneously. Throw in a database and it's actually quite simple to have the clients interact as well. (ie: One user does something, everyone sees what happened). To my knowledge and/or ability, you have to use a storage method (ie: mysql database) to get output to all clients. I got carried away and threw in a couple special escape sequences :P

    #!/usr/bin/perl -w package TheServer; use strict; use IO::Select; use Net::Server::PreForkSimple; our @ISA = qw(Net::Server::PreForkSimple); my ($client, $io); # Start the server on port 8000. # Allow 5 connections, run as user/group 'apache' TheServer->run( port => 8000, max_servers => 5, user => 'apache', group => 'apache' ); # Modified chomp() (does \r and \n) sub chom { $_[0] =~ s/\r?\n$// } # New Client Connection sub process_request { my $server = shift; $client = $server->{'server'}; $io = new IO::Select \*STDIN; print "\e[2JConnected from $client->{'peeraddr'}\r\n\r\n"; print "[$client->{'peeraddr'}] "; while (1) { return print "\r\n\r\n30 second timeout reached. Goodbye!\r\n\r\n" unless ($io->can_read(30)); chom( my $in = <STDIN> ); if ($in =~ /^(?:cls|clear)$/i) { print "\e[2J"; } elsif ($in =~ /^(?:date|time)$/i) { print "Date/time: \e[33m", scalar localtime, "\e[0m\r\n"; } elsif ($in =~ /^(?:exit|quit|logout)$/i) { return print "You have successfully logged out.\r\n\r\n"; } else { print "Sorry, I don't understand \e[33m$in\e[0m.\r\n"; } print "[$client->{'peeraddr'}] "; } }


    If the above content is missing any vital points or you feel that any of the information is misleading, incorrect or irrelevant, please feel free to downvote the post. At the same time, please reply to this node or /msg me to inform me as to what is wrong with the post, so that I may update the node to the best of my ability.

Re: writing to telnet?
by Anonymous Monk on Mar 09, 2003 at 19:10 UTC
    well, my problem is that i dont really want to make another progran to communicate with it, i was just hoping to use telnet, so i would not have to put the client program on each machine i was to use it on. Thanks for all of your tips.
      There is a Net::Telnet module, you may want to check it out on CPAN. Also, the default port for Telnet is 23, I am not sure how familiar you are with telnet, you may want to have some fun reading with Telnet Spec, IETF RFC 854
Re: writing to telnet?
by Anonymous Monk on Mar 09, 2003 at 19:30 UTC
    oh, another problem would be, i need to be able to listen and reply at the same time. The reason i am making this so me and i teacher at my school can communicate while in different classrooms.
      That's not a problem, TCP connection is full duplexed. For your application layer, you should try multi-thread, instead of fork. (Hope you are using 5.8.0)

      I have some sample code using both socket and thread:
      server.pl: use strict; use threads; use IO::Socket::INET; $| ++; my $listener = IO::Socket::INET->new(LocalPort => 3126, Listen => 5, Reuse => 1) || die "Cannot create socket\n"; my $client; my $client_num = 0; while (1) { $client = $listener->accept; threads->create(\&start_thread, $client, ++ $client_num); } sub start_thread { my ($client, $client_num) = @_; print "thread created for client $client_num\n"; while (1) { my $req; $client->recv($req, 700000); return if ($req eq ""); print $client $req; } return; } client.pl: use strict; use IO::Socket; my $server = IO::Socket::INET->new(Proto => "tcp", PeerPort => 3126, PeerAddr => "localhost", Timeout => 2000) || die "failed to connect\n"; for (1..100) { print $server $_; my $res; $server->recv($res, 70000); print $res; }
Re: writing to telnet?
by Anonymous Monk on Mar 10, 2003 at 13:26 UTC
    Well, i guess a better question for me to ask would be how would i write to a client program, not using telnet? i tried some of the ones people have made on this thread but it doesnt seem to work, and i just can't figure it out. thanks.
      I tend to like (and often use) Expect.pm (modules) to communicate with things that I wouldn't otherwise be able to script. You might want to check it out, too -- it's fairly straight-forward to use.

      -s.
Re: writing to telnet?
by Anonymous Monk on Mar 10, 2003 at 13:48 UTC
    well, here is another program i made that will write to telnet, it doesnt use fork so it cannot listen at the same time, does anybody know how i could incorporate fork into this. sorry i havent really used fork much.
    #!/usr/bin/perl use strict; use warnings; use IO::Socket; my $server = IO::Socket::INET->new ( LocalPort => 1337, Type => SOCK_STREAM, Reuse => 1, Listen => 5 ) or die "could not open port\n"; warn "server is ready for connection..... \n"; while (my $client = $server->accept()) { warn "client connected\n"; #while($line = <$client>){ #print $line;} my $line = <>; print $client $line; } close($server);