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: 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