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

I am trying to write a perl/Tk application that also acts as a socket server. The socket server accepts connections from TCP clients, and outputs data to those clients regularly. The Tk application uses fileevent() to handle multiple clients in a single process.

Output to each client is buffered, i.e. when there is data to be sent to each client I enable a 'writable' fileevent on that socket, and write data out when the socket is writable.

I am getting the following error when I try to set the 'writable' fileevent:

Tk::Error: fileno not same for read 7 and write 8 at /usr/lib/perl5/site_perl/5.8.0/i386-linux-thread-multi/Tk/Event/IO.pm line 115.

Does anyone know why this is? I'm using perl 5.8.0 and perl-Tk800.025 on RedHat Linux 9. Sample code is attached.

#!/usr/bin/perl use strict; use Tk; use IO::Socket; my $mw = MainWindow->new; my $clients = {}; $mw->title('Hello World'); $mw->Button(-text => 'Done', -command => sub { exit })->pack; # Setup server socket my $server = IO::Socket::INET->new ( LocalPort => 40000, Type => SOCK_STREAM, Reuse => 1, Listen => 10) or die "Couldn't be a tcp server on port 40000: $@\n"; # trigger on reads $mw->fileevent ($server, 'readable', \&server_read); # trigger every 1 second $mw->repeat (1000, \&event_tick); MainLoop; sub server_read { # get new client my $new_client = $server->accept (); my $socket_no = $new_client->fileno(); print "Got new client #" . $socket_no . "\n"; $clients->{$socket_no} = { -socket => $new_client, -buffer => '' }; # trigger on any read events (usually client disconnecting) $mw->fileevent ($new_client, 'readable', [\&client_read, $socket_n +o]); }; sub client_read { my ($socket_no) = @_; my ($buf, $len); my $client= $clients->{$socket_no}->{-socket}; $len = $client->sysread ($buf, 1024); print "Read $len bytes from client $socket_no\n"; if ($len == 0) { # client has disconnected print "client $socket_no has disconnected\n"; $mw->fileevent ($client, 'readable', ''); $mw->fileevent ($client, 'writable', ''); $client->close(); } else { print "received [$buf] from client\n"; }; }; sub event_tick { my $time = time; my $localtime = scalar (localtime($time)); # write to all the clients foreach my $socket_no (keys %$clients) { print "adding to buffer for client $socket_no\n"; $clients->{$socket_no}->{-buffer} .= $localtime; my $client = $clients->{$socket_no}->{-socket}; $mw->fileevent ($client, 'writable', [\&client_write, $socket_ +no]); }; }; sub client_write { my ($socket_no) = @_; my ($len); print "client $socket_no is writable!\n"; my $client = $clients->{$socket_no}->{-client}; $len = $client->syswrite ($clients->{$socket_no}->{-buffer}); print "wrote $len bytes to client $socket_no\n"; if ($len == 0) { # buffer emptied print "buffer for $socket_no emptied\n"; $clients->{$socket_no}->{-buffer} = ''; $mw->fileevent ($client, 'writable', ''); } else { # buffer partially emptied $clients->{$socket_no}->{-buffer} = substr ( $clients->{$socket_no}->{-buffer}, $len); }; };

Replies are listed 'Best First'.
Re: Tk & Socket: Tk::Error: fileno not same for read X and write Y
by pg (Canon) on Oct 14, 2003 at 17:26 UTC

    The overall design is not quite right. I cannot see the reason why Tk is involved here, at least base what you presented.

    Now under this design, your program only accepts connections when fileevent is fired, which I believe is not what you want, as most likely the client comes any time it wishes. This shall really be a simple socket server side program without Tk.

    When your program reaches that line accept socket connection, it just hangs until a client comes, this is not what should happen to a GUI application. This makes the person sitting before the computer really frustrated (at least when I was trying to play with your code).

    Multi-threading might help you (one the other hand, most likely you would need multi-thread any way, or other similar technologies, for a socket server that expects multiple clients at a single given time), but still better reconsider whether Tk is needed.

      I cannot see the reason why Tk is involved here, at least base what you presented.

      I assume that the posted code is just a reduced test-case that shows the problem, and that the OP wanted to use a GUI to present the information obtained from the clients in some way or another. Tk would seem a reasonable choice under those circumstances.

      Of course, I agree that Tk should probably be left out of handling the sockets (see my other post), if that's what you meant.

      bbfu
      Black flowers blossom
      Fearless on my breath

Re: Tk & Socket: Tk::Error: fileno not same for read X and write Y
by bbfu (Curate) on Oct 15, 2003 at 02:32 UTC

    I don't get that error under Windows using ActivePerl (5.8.0, Tk 800.024) but the code still does not work correctly. I'll try and test it under RedHat 9 later.

    The problem that I ran into is that, as pg pointed out, accept blocks. I also had the problem that the listening socket always triggers the readable event, whether a client is connecting or not. You can get around accept blocking by using select, but, then, why bother using fileevent at all? And since it appears to have issues, you'll probably be better off writing your own select code to poll the listening socket for data, and possibly the client socket as well.

    bbfu
    Black flowers blossom
    Fearless on my breath

      (I'm the original poster of the question above -- sorry, didn't notice I wasn't logged in when I pressed save.)

      pg: Yes, as bbfu guessed, the code I posted is a test-case to show the problem.

      Originally, my program read a number of sensors and then distributes the data out over sockets to multiple clients. As some of the sensors are accessed using TCP as well, the program needs to act as a socket server AND a socket-client at the same time. I therefore wrote everything around a select() in perl since I can't afford anything to block in the program. That program works fine.

      Recently, I've needed to do a simulator for the system, whereby instead of reading data from sensors and distributing it out to clients, an operator would be able to control the values on-the-fly. So I am migrating the original program into a Tk environment with a dynamic GUI front-end. As Tk has a select() at its heart, and that select() can be tapped into using fileevents, the overall model should not be much different from my original program.

      bbfu - I tried running it under Window and found the same problem of accept() blocking. Strange -- Under Redhat, the listening socket is not "readable" until a client connects, and only then does it pass control to the server_read() function, and so accept() doesn't block. My original program is able to handle multiple events without blocking. Even the Tk version on Linux works fine -- the GUI is operational and doesn't block. It only bombs with the error above when I try to set a "writable" fileevent on the client socket ... even then the MainLoop is still active and the GUI and other events go on fine (multiple clients can still connect).

Re: Tk & Socket: Tk::Error: fileno not same for read X and write Y
by dotsha (Initiate) on Oct 15, 2003 at 09:23 UTC
    This is the same program as above, but without Tk, and therefore implementing it's own select() loop and triggers to call the events.

    I wrote this in Redhat Linux 9, but it appears to runs fine even under Windows ... no blocking! Start up the script, and then in a few other windows, "telnet localhost 40000".

    Isn't using filevent under Tk essentially the same thing as what this script does?