sakoh@sraco2.us.sra.co.jp (Hiroshi &) (11/17/90)
Easy-serv library is a collection of util. routines which support to build simple server/multi-client programs. enjoy. #!/bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by sakoh@sra.co.jp on Fri Nov 16 18:08:29 MST 1990 # Contents: README.eng easy-serv.man.eng easy-serv.pl easy-ipc.pl echo x - README.eng sed 's/^@//' > "README.eng" <<'@//E*O*F README.eng//' README for easy-serv.pl : Fri Nov 9 14:46:50 MST 1990 1. MOKUTEKI - (Purpose) Easy-serv is a set of routines written in PERL to support constructing server/client programs. Although it has a limited features only, it's still useful for experimental or simple applications. A server can serve multiple clients at the same time. Tcp protocol is used for the purpose. Someone might find some conceputual similarity with Sun's RPC (Remote Procedure Call). However there is no XDR (eXternal Data Representation) supports (sorry). 2. RIYOU JOUKEN TOU - (Copyright notice) Copyright 1990 Software Research Associates, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of S.R.A. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. S.R.A. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. S.R.A. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL S.R.A. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Author: Sakoh, Hiroshi - Software Research Associates, Inc. @//E*O*F README.eng// chmod u=rw,g=rw,o=r README.eng echo x - easy-serv.man.eng sed 's/^@//' > "easy-serv.man.eng" <<'@//E*O*F easy-serv.man.eng//' @.TH EASY-SERV l "Nov 1, 1990" @.SH NAME easy-serv \- simple server/multi-client program library in perl. @.SH SYNOPSIS @.br @.B require """easy-serv.pl"" @.br @.B "®ister_serv(socket, port)" @.br @.B "&run_serv()" @.br @.B "&cleanup_serv()" @.br @.B "®ister_client(socket, host, port)" @.br @.B "&send_request(socket, request)" @.br @.SH DESCRIPTION Easy-serv is a set of routines to support constructing server/client programs. Although it has a limited features only, it's still useful for experimental or simple applications. a server can serve multiple clients at the same time. Tcp protocol is used for the purpose. Someone might find some conceputual similarity with Sun's RPC (Remote Procedure Call). However there is no XDR (eXternal Data Representation) supports (sorry). @.PP Routines ®ister_serv(), &run_serv(), &cleanup_serv() are used in server side. The rest ®ister_client(), &send_request() are used in client side. @.TP @.B "®ister_serv(SOCKET, PORT)" creates a internet tcp/ip SOCKET and bind it to the specified PORT. This SOCKET will be used as server socket. You can specify PORT as number or a symbolic name found in /etc/service. @.TP @.B "&run_serv()" processes all requests from multiple clients. Before calling this routine, you must call ®ister_serv() to have a server socket. Users must supply a routine named @.B "&serv_body(REQUEST, SOCKET)" which does actual services. In REQUEST argument, you will find data from the current client. And in SOCKET argument, you have the current client's socket name. Each client's socket name is made to be unique within a server so that you can distinguish clients within &serv_body() routine. For example, if you make a server socket by ®ister_serv(SERVER, 79999), consequently each client's socket has a name like "SERVER..a", "SERVER..b" .. and so on. The return value of &serv_body() is sent back to the current client as it is. See the example described below. @.TP @.B "&cleanup_serv()" closes all sockets and exits. This is used in &serv_boody() routine. @.PP The following routines are used in clients. @.TP @.B "®ister_client(SOCKET, HOST, PORT)" makes a client side SOCKET connecting to the specific PORT on the specific HOST. For HOST, you can supply a name found in /etc/hosts or a internet address format like 11.22.33.44. PORT's format is same as ®ister_serv(). @.TP @.B "&send_request(SOCKET, REQUEST)" sends an REQUEST to the corresponding server via SOCKET which is created by ®ister_client() call. It waits a reply from server and returns it. @.PP Easy-serv library routines rely on the routines defined in easy-ipc.pl. You might be able to build more sophisticated applications by using them directly. @.SH EXAMPLE @.sp A server @.sp @.nf @.ft LP #!/usr/local/bin/perl require "easy-serv.pl"; # loading the library ®ister_serv(SERVER, 7999); # make a server socket on port 7999 &run_serv(); # dispatching (never returns) sub serv_body { # Users must supply this routine. # $_[0] : request data sent from a client # $_[1] : socket name corresponding to the current client print "client sock name=" . $_[1] . "\n"; if ($_[0] eq "who") { $reply = `who`; } elsif ($_[0] eq "ps") { $reply = `ps`; } elsif ($_[0] eq "kill") { &cleanup_serv(); } else { $reply = "who : who is on this host\\n" . "ps : show process\\n" . "kill : kill server\\n" . "help : show this message\\n"; } $reply; # This value is sent back to the current client } @.ft P @.fi @.sp A client @.sp @.nf @.ft LP #!/usr/local/bin/perl require "easy-serv.pl"; # loading the library ®ister_client(CLIENT, "myhost", 7999); # connecting to port 7999 on "myhost" $| = 1; print "-> "; while(print "-> ", <STDIN>) { chop; $reply = &send_request(CLIENT, $_); # send a request print $reply; # print the reply } @.ft P @.fi @.SH FILES /usr/local/lib/perl/easy-serv.pl : easy-serv library @.br /usr/local/lib/perl/easy-ipc.pl : low level util. library @.br /usr/local/lib/perl/sys/socket.ph (not included) @.br /usr/local/lib/perl/sys/fcntl.ph (not included) @.SH "IMPORTANT NOTE" @.SH BUGS Package isn't used (yet). @.br Tested only on perl v3.0 patchlevel > 37. @.SH AUTHOR Sakoh, Hiroshi: Software Research Associates, Inc. @.br sakoh@sra.co.jp or uunet!sra.co.jp!sakoh @//E*O*F easy-serv.man.eng// chmod u=rw,g=rw,o=r easy-serv.man.eng echo x - easy-serv.pl sed 's/^@//' > "easy-serv.pl" <<'@//E*O*F easy-serv.pl//' ###### # # EASY-SERV.PL # Copyright : Software Research Associates, Inc. 1990 # Written by Hiroshi Sakoh (sakoh@sra.co.jp) on 11-01-90 # Please distribute widely, but leave my name here. # require "sys/fcntl.ph"; require "easy-ipc.pl"; # # ®ister_serv($name, $port) # # Registers a service port. # sub register_serv { &defserver($_[0], $_[1]); fcntl($_[0], &F_SETFL, &FNDELAY) || die "fcntl: $!\n"; $_eserv_generic{$_[0]} = $_[0]; # define generic socket $_eserv_sockets{$_[0]} = $_[0]; } # # ®ister_client($name, $host, $port) # # Registers a client port. # sub register_client { &defclient($_[0], $_[1], $_[2]); fcntl($_[0], &F_SETFL, &FNDELAY) || die "fcntl: $!\n"; $_eclient_sockets{$_[0]} = $_[0]; } # # &run_serv() # # Wait for a request, dispatch it. # Sends back a reply to the requester. # sub run_serv { local($bytestoread, $packet, $actualread, $request, $reply); local($bytestowrite, $actualwrite); local($try); local($MAXTRY) = 1000; for (;;) { @avails = &selectsock(keys(_eserv_sockets)); # wait for a request nextavail: for(@avails) { if (defined($_eserv_generic{$_})) { # got a request for connection &acceptsock($_eserv_newsock, $_); $_eserv_sockets{$_eserv_newsock} = $_eserv_newsock; } else { if (!eof($_)) { # got an usual request chop($bytestoread = <$_>); # The first packet should contain bytes to be read. for ( $request = "" ; $bytestoread > 0 ; $bytestoread -= $actualread) { for ($actualread = $try = 0; ($actualread == 0) && ($try < $MAXTRY); $try++) { $actualread = read($_, $packet, $bytestoread); } if ($actualread == 0) { print STDERR "Unexpected EOF\n"; close($_); delete $_eserv_sockets{$_}; next nextavail; } $request .= $packet; } $reply = &serv_body($request, $_); # I'd really like to use syswrite() below to # avoid to be blocked. But it doesn't work. Why?????? print $_ length($reply) . "\n"; for ($bytestowrite = length($reply) ; $bytestowrite > 0 ; $bytestowrite -= 1024) { print $_ substr($reply, 0, 1024); $reply = substr($reply, 1024); } } else { # got an eof message close($_); delete $_eserv_sockets{$_}; } } } } } # # &send_request($socket, $request) # # Sends a request to a server, waits a reply from the server # and returns it. # sub send_request { local($sock, $request) = @_; local($reply, $packet, $bytestoread, $actualread); local($bytestowrite); local($try); local($MAXTRY) = 1000; # I'd really like to use syswrite() below to # avoid to be blocked. But it doesn't work. Why?????? print $_ length($request) . "\n"; # write length of data for ($bytestowrite = length($request) # write data packets ; $bytestowrite > 0 ; $bytestowrite -= 1024) { print $sock substr($request, 0, 1024); $reply = substr($request, 1024); } chop($bytestoread = <$sock>); # The first packet should contain bytes to read. for ( $reply = "" ; $bytestoread > 0 ; $bytestoread -= $actualread) { for ($actualread = $try = 0; ($actualread == 0) && ($try < $MAXTRY); $try++) { $actualread = read($_, $packet, $bytestoread); } if ($actualread == 0) { print STDERR "Unexpected EOF\n"; close($sock); return ""; } $reply .= $packet; } $reply; } # # &cleanup_serv() # # Closes down all server sockets and exits. # sub cleanup_serv { for(keys(_eserv_sockets)) { close($_); } exit; } 1; @//E*O*F easy-serv.pl// chmod u=r,g=r,o=r easy-serv.pl echo x - easy-ipc.pl sed 's/^@//' > "easy-ipc.pl" <<'@//E*O*F easy-ipc.pl//' ###### # # EASY-IPC.PL # Copyright : Software Research Associates, Inc. 1990 # Written by Hiroshi Sakoh (sakoh@sra.co.jp) on 10-25-90 # Please distribute widely, but leave my name here. # require "sys/socket.ph"; # # &defserver(SOCKET, $port); # # Returns a server socket ready for acceptsock() # sub defserver { # arg0 == socket, arg1 == port local($this, $oldfh); local($port) = $_[1]; local($name, $aliases, $proto); local($sockaddr_t) = 'S n a4 x8'; ($name, $aliases, $port, $proto) = getservbyname($port, "tcp") unless $port =~ /^\d+$/; socket($_[0], &PF_INET, &SOCK_STREAM, 0) || die "socket: $!\n"; $this = pack($sockaddr_t, &PF_INET, $port, "\0\0\0\0"); bind($_[0], $this) || die "bind: $!\n"; listen($_[0], 5); $oldfh = select($_[0]) ; $| = 1; select($oldfh); } # # &defclient(SOCKET, $host, $port) # # Returns a connected client socket. # sub defclient { # arg0 == socket, arg1 == host, arg2 == port local($that, $thataddr, $oldfh); local($port) = $_[2]; local($name, $aliases, $proto); local($sockaddr_t) = 'S n a4 x8'; ($name, $aliases, $port, $proto) = getservbyname($port, "tcp") unless $port =~ /^\d+$/; if ($_[1] =~ /^\d+\.\d+\.\d+\.\d+$/) { $thataddr = &inet_addr($_[1]); } else { ($name, $aliases, $type, $len, $thataddr) = gethostbyname($_[1]); } $that = pack($sockaddr_t, &PF_INET, $port, $thataddr); socket($_[0], &PF_INET, &SOCK_STREAM, 0) || die "socket: $!\n"; connect($_[0], $that) || die "connect: $!\n"; $oldfh = select($_[0]) ; $| = 1; select($oldfh); } $__EIPCSno__ = 'a'; # # &acceptsock($newsock, $serversock) # # Returns a bind socket which is derived from a generic server socket # sub acceptsock { local($addr); local($oldfh); $_[0] = $_[1] . ".." . $__EIPCSno__ ++; # make it unique $addr = accept($_[0], $_[1]) || die "accept: $!\n"; $oldfh = select($_[0]) ; $| = 1; select($oldfh); $addr; } # # &selectsock(@sockets) # # Returns available sockets for read (blocks) # sub selectsock { &selectsock_with_timeout(undef, @_); } # # &selectsock_non_block(@sockets) # # Returns available sockets for read (doesn't block) # sub selectsock_non_block { &selectsock_with_timeout(0, @_); } # # &selectsock_with_timeout($timeout, @sockets) # # Returns available sockets for read (with timeout) # sub selectsock_with_timeout { local($nfound, $timeleft); local($rin, $rout, $win, $wout, $eout, $ein); local($timeout) = shift; $rin = &setsockbits(@_); ($nfound, $timeleft) = select($rout=$rin, $wout=$win, $eout=$ein, $timeout); grep(vec($rout,fileno($_),1),@_); } # # &setsockbits(@sockets) # # Returns a bitvector corresponding to sockets list # sub setsockbits{ local(@fhs) = @_; local($bits); for (@fhs) { vec($bits, fileno($_), 1) = 1; } $bits; } # # &inet_addr($addr) # # Converts an internet dot notation (##.##.##.##) to an internet address # sub inet_addr { pack("C4", split(/\./, $_[0])); } 1; @//E*O*F easy-ipc.pl// chmod u=r,g=r,o=r easy-ipc.pl exit 0 -- sakoh@sra.co.jp "Whereof one cannot speak, thereof one must remain silent." ---Wittgenstein "Sometimes noise is significant." ---William Hewlett