Article 7987 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:7987
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!howland.reston.ans.net!europa.eng.gtefsd.com!uunet!munnari.oz.au!metro!usage!news
From: cameron@cse.unsw.edu.au (Cameron Simpson)
Subject: Re: Sockets and stuff question...
Message-ID: <cameron-931116200252-1-17925@crwth>
To: jimh@Rational.COM (Jim Hamilton)
Followup-To: comp.lang.perl
Sender: news@usage.csd.unsw.OZ.AU
Nntp-Posting-Host: 149.171.200.12
Reply-To: cameron@cse.unsw.edu.au
Organization: CS&E Computing Facility, Uni Of NSW, Oz
References: <1993Nov10.150746.10961@rational.com>
Errors-To: cameron@cse.unsw.edu.au
Date: Tue, 16 Nov 1993 09:03:05 GMT
Return-Receipt-To: cameron@cse.unsw.edu.au
Lines: 271

jimh@Rational.COM (Jim Hamilton) writes:
| 1) In my reading of "the book", I am left with the impression that a socket
| should be bi-directional, is this true and how do you use the other 
| direction?

Yes. Well, you could just try reading from it (or writing if you were
reading); you can use the one stream. You may need to flush the stream
in between. To save that hassle I tend to dup the stream:

	# assume TO has been bound, connected, etc
	open(FROM,"<&TO") || die "blah blah blah: $!";

This makes two stdio streams.

| 2) Is there a cool way of catching the output of a command executed by 
| system() other than the very crude way I did it. I appended to the command
| a redirection into a temp file which I later reopen to read and send 
| back across the socket so the client can print out the results.

`command` ?

Or, if you want pure command output fed back to the client:

	if (fork == 0)
		# child
		{ open(STDOUT,">&SOCKET") || die ...
		  close(SOCKET);	# handle now on STDOUT
		  open(STDERR,">&SOCKET") # you may not want this
		  exec(command args)
		  die "exec fails: $!\n";
		}

| 3) In order to allow multiple clients to call the server I implemented
| a crude protocol, which is the client sends his hostname across the 
| socket before the command, so the server knows who to later connect to.

But if you use the connection both ways the server doesn't need to make
another connection, nor to trust the hostname/port coming from the client...

| The funky thing is the name appears to be interpreted as a character
| not a string. Assuming the hostname is "poobah" saved into $them, and 
| is read in on the server side from the socket, if I print($them) I get 
| "poobah", if I chop($them) it I get "", if I just us it in gethostbyname($them) 
| (as in the client pg. 343) I don't get the correct results. BUT if I assign 
| $bogus = chop($them);, then use $them in the gethostbyname$them(), it works, 
| What is going on here?

Not sure, really; in case you're curious I append the TCP wrappers I use.
They're a combination of the Book and some C wrappers I have I adapted
(i.e. the book's calls, my subroutine interface).
	- Cameron Simpson
	  cameron@cse.unsw.edu.au, DoD#743
--
Automobile: device used to drive slower than motorcycles.


#!/bin/sh
#

sed 's/^X//' > tcp.pl <<'EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/tcp.pl'
X#!/usr/local/bin/perl
X
Xrequire 'sys/socket.ph';
Xrequire 'cs/net.pl';
X
Xpackage tcp;
X
X{ local($name,$aliases);
X
X  ($name,$aliases,$proto)=getprotobyname('tcp');
X  die "$0: can't look up tcp protocol" unless defined($proto);
X}
X
X$SOCK='TCPSOCK0000';
Xsub tcp'rwopen	# (host,port) -> (FILE)
X	{ local($rhost,$port)=@_;
X	  local($name,$aliases,$type,$len,$raddr);
X	  local($dummy);
X
X	  ($port,$dummy)=&net'service($port,'tcp')
X		unless $port =~ /^\d+$/;
X	  ($name,$aliases,$type,$len,$raddr)=gethostbyname($rhost);
X
X	  local($local,$remote);
X	  $local =&net'mkaddr_in(0,$net'hostaddr);
X	  $remote=&net'mkaddr_in($port,$raddr);
X
X	  local($sockf)=$SOCK++;
X
X	  ((warn "socket: $!"), return undef)
X		unless socket($sockf,&main'AF_INET,&main'SOCK_STREAM,$tcp'proto);
X
X	  ((warn "$!"), close($sockf), return undef)
X		unless bind($sockf,$local) && connect($sockf,$remote);
X
X	  local($s)=select($sockf); $|=1; select($s);
X
X	  "tcp'".$sockf;
X	}
X
Xsub tcp'rwopen2 # (host,port) -> (FROM,TO)
X	{ local($TO)=&rwopen;
X
X	  return undef unless defined($TO);
X
X	  local($FROM);
X	  $FROM="tcp'".$tcp'SOCK++;
X	  (close($TO), return undef) unless open($FROM,'<&'.fileno($TO));
X
X	  ($FROM,$TO);
X	}
X
Xsub tcp'bind	# (port) -> FILE
X	{ local($port)=@_;
X	  local($name,$aliases);
X	  local($FILE,$dummy);
X
X	  ($port,$dummy)=&net'service($port,'tcp')
X		unless $port =~ /^\d+$/;
X
X	  $FILE=$SOCK++;
X	  ((warn "socket: $!"), return undef)
X		unless socket($FILE, &main'PF_INET, &main'SOCK_STREAM, $tcp'proto);
X
X	  $name=&net'mkaddr_in($port, "\0\0\0\0");
X	  ((warn "bind: $!"), return undef)
X		unless bind($FILE, $name);
X
X	  listen($FILE,10) || warn("listen($FILE,10): $!");
X
X	  "tcp'".$FILE;
X	}
X
X1;
EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/tcp.pl

sed 's/^X//' > udp.pl <<'EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/udp.pl'
X#!/usr/local/bin/perl
X#
X# Package for UDP connections:
X#
X#	$udp'proto		Protocol number for UDP.
X#	&udp'bind(port) -> FILE	Make a UDP socket at port (0 for any)
X#
X
Xrequire 'sys/socket.ph';
Xrequire 'cs/net.pl';
X
Xpackage udp;
X
X{ local($name,$aliases);
X
X  ($name,$aliases,$proto)=getprotobyname('udp');
X  die "$0: can't look up udp protocol" unless defined($proto);
X}
X
X$SOCK='UDPSOCK0000';
Xsub udp'bind	# (port) -> FILE
X	{ local($port)=@_;
X	  local($name,$aliases);
X	  local($FILE,$dummy);
X
X
X	  ($port,$dummy)=&net'service($port,'udp')
X		unless $port =~ /^\d+$/;
X	  $FILE=$SOCK++;
X	  ((warn "socket: $!"), return undef)
X		unless socket($FILE, &main'PF_INET, &main'SOCK_DGRAM, $udp'proto);
X
X	  $name=&net'mkaddr_in($port, "\0\0\0\0");
X	  ((warn "bind: $!"), return undef)
X		unless bind($FILE, $name);
X
X	  "udp'".$FILE;
X	}
X
Xsub udp'send	# (sock,data,port,addr) -> chars sent or undef
X	{ local($SOCK,$_,$port,$addr)=@_;
X
X	  $_=send($SOCK,$_,0,&net'mkaddr_in($port,$addr));
X
X	  defined($_) ? $_ : undef;
X	}
X
Xsub udp'recv	# ($SOCK) -> ($data,$port,$addr) or undef
X	{ local($_,$from);
X
X	  $from=recv(shift,$_,65536,0);
X
X	  return undef if !defined($from);
X
X	  local($family,$port,$addr)=unpack($net'sockaddr,$from);
X
X	  ($_,$port,$addr);
X	}
X
X1;	# for require
EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/udp.pl

sed 's/^X//' > net.pl <<'EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/net.pl'
X#!/usr/local/bin/perl
X#
X# Package for network stuff.
X#
X#	$net'sockaddr		Pack/Unpack format for a sockaddr.
X#	$net'hostname		Local host name.
X#	$net'hostaddr		Local host address.
X#	&net'getaddr(SOCK) -> (family,port,addr)
X#				Return address of socket.
X#	&net'mkaddr(family,port,addr) -> sockaddr
X#				Produce machine socket address.
X#	&net'mkaddr(port,addr) -> sockaddr_in
X#				Produce machine socket internet address.
X#	&net'addr2a(addr) -> "x.x.x.x" Produce decimal rep of address.
X#
X
Xrequire 'sys/socket.ph';
X
Xpackage net;
X
X{ local($name,$aliases,$type,$len);
X
X  $sockaddr='S n a4 x8';
X  chop($hostname=`hostname`);
X  die "$0: can't look up hostname" unless length($hostname);
X  ($name,$aliases,$type,$len,$hostaddr)=gethostbyname($hostname);
X  die "$0: can't look up hostaddr($hostname)" unless defined($name);
X}
X
Xpackage main;
X
Xsub net'service	# (servname,protocolname) -> (port-number,proto-number)
X	{ local($serv,$proto)=@_;
X	  local($protoname,$etc1,$etc2);
X
X	  if ($proto !~ /^\d+$/)
X		{ ($protoname,$etc2,$proto)=getprotobyname($proto);
X		  # print STDERR "byname: name=$protoname, num=$proto\n";
X		}
X	  else
X	  { ($protoname,$etc2,$proto)=getprotobynumber($proto);
X	    # print STDERR "bynumber: name=$protoname, num=$proto\n";
X	  }
X
X	  ($etc1,$etc2,$serv)=getservbyname($serv,$protoname)
X		unless $serv =~ /^\d+$/;
X
X	  # print STDERR "net'service(@_) -> ($serv $proto)\n";
X	  ($serv,$proto);
X	}
X
Xsub net'getaddr	# (SOCK) -> (family,port,myaddr)
X	{ unpack($net'sockaddr,getsockname($_[$[]));
X	}
X
Xsub net'mkaddr_in # (port,address) -> sockaddr_in
X	{ &net'mkaddr(&main'AF_INET,@_);
X	}
X
Xsub net'mkaddr	# (family,port,address) -> sockaddr
X	{ pack($net'sockaddr,@_);
X	}
X
Xsub net'addr2a	# address -> "x.x.x.x"
X	{ sprintf("%d.%d.%d.%d",unpack("CCCC",shift));
X	}
X
X1;
EOF-/home/cs/orchestra/crwth/1/cameron/etc/pl/cs/net.pl

exit 0