Date post: | 15-Jan-2016 |
Category: |
Documents |
View: | 220 times |
Download: | 0 times |
Chapter 4
http://www.lonsteins.com/articles/sockets.pdf
A Tourist’s Guide to Socket Programming in Perl
TCP Protocol:
• Typical TCP client program
• call socket() to create socket• call connect() to connect to peer• send and receive data• close the socket.sets up socket
structure on this side of connection
starts three-wayhandshake and waitsfor completion
sends FIN flag fromthis side of connection
echo client 1 (1):
#!/usr/bin/perl# file: tcp_echo_cli1.pl# Figure 4.1: A TCP Echo Client
# usage: tcp_echo_cli1.pl [host] [port]
use strict;use Socket;use IO::Handle;my ($bytes_out,$bytes_in) = (0,0);
my $host = shift || 'localhost';my $port = shift || getservbyname('echo','tcp');
my $protocol = getprotobyname('tcp');$host = inet_aton($host) or die "$host: unknown host";
echo client 1 (2):
socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";my $dest_addr = sockaddr_in($port,$host);connect(SOCK,$dest_addr) or die "connect() failed: $!";
SOCK->autoflush(1);
while (my $msg_out = <>) { print SOCK $msg_out; my $msg_in = <SOCK>; print $msg_in;
$bytes_out += length($msg_out); $bytes_in += length($msg_in);}
close SOCK;print STDERR "bytes_sent = $bytes_out, bytes_received = $bytes_in\n";
TCP function:
• Given all four arguments, socket() creates a local socket and associates it with the handle.
• On error, returns undef. • Typical idiom is
$boolean = socket(SOCKET,$domain,$type,$protocol);
socket(SOCK,AF_INET,SOCK_STREAM, scalar getprotobyname(‘tcp’))
# AF_INET, SOCK_STREAM defined in Socket # the last three arguments are all small integers
TCP client function:
• Used with TCP. SOCKET must exist and address is packed and built with sockaddr_in() function.
• On error, returns false and $! has error number. • Local port is chosen at from ephemeral port numbers.• Illegal to call connect() twice on same socket handle. if
done, returns EISCONN (“Transport is endpoint already connected”).
• Starts three-way handshake process and waits until completed one way of the other.
$boolean = connect(SOCKET,$dest_addr);
TCP functions (cont):
• Works with sockets as well as files. After closing a socket it can neither be written to nor read from.
• On error, returns undef and $! has error number. • At the remote end of the connection reading will result in
EOF while writing results in broken PIPE signal. • The broken PIPE signal is received at remote end
regardless of additional write by remote end.• Does not parallel actual socket behaviour where both
ends must send a FIN flag and sending a FIN only means you will not write any more. You may still read.
$boolean = close(SOCKET);
What happens with close(SOCK):
application
tcp
socket: connection to application closedclose(SOCK)
send FIN: theoretically can still receive data
any data that arrives later, TCPknows the socket is closed andso replies as though it were neveropen in the first place by sendinga RST. No automatic RST.
data arrivesafter close()
TCP/IPStack
close(SOCKET);
• Works as expected!!
printf "shutting down...\n";close(SOCK); # close for read$msg_in = <SOCK>;if ( ! defined $msg_in ) { printf "read failed\n";} else { print "read: $msg_in\n"; }
print SESSION $msg_out; $bytes_out += length($msg_out); last; } sleep(1); print SESSION "again: $msg_out\n"; # print a second time
sever code
client code
this line executed:
asd dsa shutting down... read failed
first server send
second server send
close() ethereal dump:
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
client: port 35192server: port 2007
packet 10: server sends 12 more char
packet 11: client sends RST
packet 8: close first to FIN
TCP function (cont):
• Can more precisely mirror TCP behaviour.• $how = 0: Closes socket for reading.• $how = 1: Closes socket for writing (TCP behaviour).• $how = 2: Closes socket for reading and writing (like
close())
$boolean = shutdown(SOCKET,$how);
shutdown(SOCKET,0);
• Does not work as advertised!!
printf "shutting down...\n";shutdown(SOCK,0); # close for read$msg_in = <SOCK>;if ( ! defined $msg_in ) { printf "read failed\n";} else { print "read: $msg_in\n"; }
print SESSION $msg_out; $bytes_out += length($msg_out); last; } sleep(1); print SESSION "again: $msg_out\n"; # print a second time
server code
client code
this line executed:
asd dsa shutting down... read: again: dsa
first server send
second server send
ethereal dump:
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
client: port 35188server: port 2007
packet 8: server sends 12 more char
packet 9: client ACKs 12 char
packet 10: server first to FIN
shutdown(SOCKET,1):
• Works as advertised.• Client sends FIN and waits for FIN/ACK.• This is the best option to mimic the behaviour of TCP in
your own application.
shutdown(SOCKET,2);
• Works as advertised.
printf "shutting down...\n";shutdown(SOCK,2); # close for read/write$msg_in = <SOCK>;if ( ! defined $msg_in ) { printf "read failed\n";} else { print "read: $msg_in\n"; }
print SESSION $msg_out; $bytes_out += length($msg_out); last; } sleep(1); print SESSION "again: $msg_out\n"; # print a second time sleep(3); print SESSION "again: $msg_out\n"; # print a second time
server code
client code
this line executed:
asd dsa shutting down... read failed
first server send
second server send
third server send
NOTE: 2nd server write sends anddid not cause error; BROKENPIPE SIGNAL arrived later. 3rd server write resulted in errorand no TCP send occurs
ethereal dump:
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
client: port 35205server: port 2007
packet 10: server sends 12 char
packet 9: client ACKs 12 char
packet 8: client sends FIN
packet 11: client sends RST
TCP Server:
• TCP servers typically start up and wait for someone to ask for service.
• They do not call connect().• The sequence of function calls is:
socket(); # creates a local socketbind(); # binds to passed port numberlisten(); # listens for incoming connection requestsaccept(); # accepts incoming connection request
Connection schematic:
client
server
socket();bind();listen();
listeningsocket
listeningsocket
accept();
connectedsocket
separate file descriptor
acceptloop
connect()
establishedconnection
1st
2nd
3rd4th
blocks untilconnectionrequest arrives
spawns newconnected socket
TCP Server Example (1):#!/usr/bin/perl# file: tcp_echo_serv1.pl# usage: tcp_echo_serv1.pl [port]use strict; use Socket; use IO::Handle;use constant MY_ECHO_PORT => 2007;$SIG{'INT'} = \&int_handler;
my ($bytes_out,$bytes_in) = (0,0);my $port = shift || MY_ECHO_PORT;my $protocol = getprotobyname('tcp');
socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";setsockopt(SOCK,SOL_SOCKET,SO_REUSEADDR,1) or die "Can't set SO_REUSADDR: $!" ;
my $my_addr = sockaddr_in($port,INADDR_ANY);bind(SOCK,$my_addr) or die "bind() failed: $!";listen(SOCK,SOMAXCONN) or die "listen() failed: $!";
TCP Server Example (2):warn "waiting for incoming connections on port $port...\n";while (1) { next unless my $remote_addr = accept(SESSION,SOCK); my ($port,$hisaddr) = sockaddr_in($remote_addr); warn "Connection from [",inet_ntoa($hisaddr),",$port]\n";
SESSION->autoflush(1); while (<SESSION>) { $bytes_in += length($_); chomp; my $msg_out = (scalar reverse $_) . "\n"; print SESSION $msg_out; $bytes_out += length($msg_out); } warn "Connection from [",inet_ntoa($hisaddr),",$port] finished\n"; close SESSION;}close SOCK;sub int_handler{ print STDERR "bytes_sent = $bytes_out, bytes_received = $bytes_in\n"; exit 0;};
Server Socket Functions 1:
• Binds a local address (IP,Port) to SOCK (previously created with socket()).
• $my_addr is packed and created with sockaddr_in() or its equivalent.
• IP address can be any of the host’s IP addresses, the loopback or the wildcard INADDR_ANY.
• Root access required to open ports under 1024.• Returns undef if it fails and $! == EACCES (“Permission
Denied”).• Normally used by server to select the port it wishes to
use. Client uses bind() when local port is specified (eg, SIP client and server both use port 5070
$boolean = bind(SOCK,$my_addr);
Server Socket Functions 2:
• A socket is opened for accepting incoming connection requests only. It does not have a specified remote end.
• SOCK is created with socket().• $max_queue is the maximum allowable number of
completed 3-way handshakes that have not been already accept()ed. This can not exceed a system max.
• Socket::SOMAXCONN is the system number.• On failure it returns undef and $! contains the error.
$boolean = listen(SOCK,$max_queue);
Server Socket Functions 3:
• Used with a listening socket to accept new connections.• LISTEN_SOCK created with socket() and listen().• On success, it returns a packed remote address as
produced by sockaddr_in(). CONN_SOCK is associated with this new address.
• accept() blocks if no completed 3-WHS is pending acceptance.
• returns undef on error and $! can be any one of a number of errors.
$boolean = accept(CONN_SOCK,LISTEN_SOCK);
Server Socket Functions 4:
• Used to recover the local and remote addresses associated with a socket.
• Returns a packed binary address that must be unpacked using sockaddr_in() and inet_ntoa() (for the IP address).
$my_addr = getsockname(SOCK);$remote_addr = getpeername(SOCK);
if ( $remote_addr = getpeername(SOCK)) { my ($port,$IP) = sockaddr_in($remote_addr); my $host = gethostbyname($IP,AF_INET); printf “Socket connected to $host at port $port\n”;}
Problems with tcp_echo_serv1.pl:
• Server can accept only one incoming connection at a time. Must complete service of one connection before accepting a second. This problem is avoided with threads or by forking or by “multiplexing” the server IO operations.
• Server runs in the foreground. ^C can kill it.• There is little or no logging.
Socket Options:
• Sockets have characteristics (called options) that are modifiable.
• $level refers to level of the protocol stack the option refers to (SO_SOCKET is suitable for most options).
• Some times we want to adjust TCP itself or UDP and not this particular socket. In that case use the protocol number that comes from getprotobyname().
• $option_name is an integer value; a large list exists.• $option_value is the new value for the option.• both functions return undef on failure.
$value = getsockopt(SOCK,$level,$option_name);$boolean = setsockopt(SOCK,$level,$option_name,$option_value);
Adjusting Socket Options:
• If the option value is boolean then use 1 or 0 for $option_value.
• Not very useful for TCP, we don’t often broadcast using that protocol.
• Some values passed to setsockopt() are packed binary
setsockopt(SOCK,SO_SOCKET,SO_BROADCAST,1);my $reuse = getsockopt(SOCK,SO_SOCKET,SO_BROADCAST);
$sz = unpack(“I”,getsockopt(SOCK,SOL_SOCKET,SNDBUF));
Common Socket Options:
SO_REUSEADDR Rebind to local address immediately.
SO_KEEPALIVE Enable “keepalive” ACKs.
SO_LINGER Block close() if data to send still pending.
SO_BROADCAST UDP only. Allows broadcast destination address.
SO_OOBINLINE Out-of-band data sent as urgent with urgent ptr.
SO_SNDLOWAT Minimum syswrite() write size w/o blocking (1).
SO_RECVLOWAT Minimum sysread() read size w/o blocking (1).
SO_TYPE Read-only. Returns packed SOCK_DGRAM.
SO_ERROR Read-only. Returns last error code. Clears.
setsockopt(SOCK,SO_SOCKET,SO_LINGER, pack(“II”,1,120));
sets a socket to linger for 120 seconds
Most Common Option (SO_REUSEADDR):
• A port in TIME_WAIT can not be immediately reused.• Setting this option lets a server that crashes reuse its
well-known port without waiting.• bind() fails early on unless this option set.
• You can have multiple processes bound to the same socket. They compete for incoming connections. Must be the same user.
setsockopt(SOCK,SO_SOCKET,S_REUSEADDR,1) or die “..”;bind(SOCK,$my_addr);
fcntl() and ioctl() funcions:
• Alternative way to handle socket options.• We use this in Chapter 13 (non-blocking IO).
Other socket operations:
• Queues $data with TCP or UDP for transmission.• Returns number of bytes queued (sent).• Ignore $flags for now.• $destination only valid for UDP where you can redirect
data to different receivers.• On TCP, send() like syswrite().
$bytes = send(SOCK,$data,$flags[,$destination]);
Other socket operations:
• Accepts up to $length data into $buffer.• $buffer grows or shrinks as needed (no buffer overflow).• Ditto $flags. Set it to 0.• Returns packed address of message sender on success.• Returns undef on failure and $! is set appropriately• On TCP, recv() like sysread() except it returns sender
address.• Used mostly for UDP.
$bytes = recv(SOCK,$buffer,$length,$flags);
Other socket operations:
• Creates two unnamed sockets connected end to end.• Arguments have same meaning as socket()..• Returns undef on failure and $! is set appropriately• Returns true with two open sockets; no need to connect.• Both sockets will be on the same machine.• Similar to pipe() but bidirectional.• Most often used with a fork(); parent closes one socket,
child the other.
$boolean = socketpair(SOCK_A,SOCK_B,$domain,$type,$protocol);
socketpair(SOCK_A,SOCK_B,AF_UNIX,SOCK_STREAM,PF_UNSPEC);
most common invocation
End-of-line:
• Special characters exported by the Socket module• Network preference is to end lines with CRLF (“\r\n” or
just “\n”).
• Socket exports $CRFL, $CR and $LF along with constants CRLF, CR and LF.
$/ = “\015\012”; # gives you control of what to expect
use Socket qw(:DEFAULT :crlf);
need this tag to get these values
Connect() Exceptions:
• TCP is meant to be robust even under the weakest network conditions (nuclear attack!!).
• Still needs to be able to report it “failed” at some level.• connect() exceptions are:
– ECONNREFUSED: Remote host up, no listening server (RST)– ETIMEOUT: Remote host down; connection request times out
(Silence).– ENETUNREACH: Network routing failed. (ICMP)– ENOTSOCK: Programmer error; eg, used file handle instead.
This error also returned by bind(), listen(), accept() and sockopt().
Read/Write Exceptions:
• What if server program crashes and your connection is broken?– On read you get EOF; on write you get EPIPE exception.
Handling the EPIPE signal means print() or syswrite() return false and $! == “Broken Pipe”;
• What if server host crashes while connected to it?– You can’t tell dead host from long network outage. You continue
to write but receive no ACKs. Eventually read() or write() will block.
– Once the host is back up and it gets your data (but has forgotten your connection) it will send RST. Then it looks like a server program crash.
– You can use SO_KEEPALIVE to have your socket give up earlier.
Read/Write Exceptions (continue):
• What if the network goes down while connection is established?– In this case IO may block but once network connectivity is
restored, things go on as normal.– If a network being down causes another network router to send
an ICMP “network unreachable” error you will get EOF or EPIPE depending on what you are doing.