Transcript Socket
Chapter 4
http://www.lonsteins.com/articles/sockets.pdf
A Tourist’s Guide to Socket Programming in Perl
TCP Protocol:
• Typical TCP client program
sets up socket
structure on this
side of connection
• call socket() to create socket
• call connect() to connect to peer
• send and receive data
• close the socket.
sends FIN flag from
this side of connection
starts three-way
handshake and waits
for completion
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:
$boolean = socket(SOCKET,$domain,$type,$protocol);
• Given all four arguments, socket() creates a local socket
and associates it with the handle.
• On error, returns undef.
• Typical idiom is
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:
$boolean = connect(SOCKET,$dest_addr);
• 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.
TCP functions (cont):
$boolean = close(SOCKET);
• 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.
What happens with close(SOCK):
TCP/IP
Stack
application
close(SOCK)
socket: connection to application closed
tcp
send FIN: theoretically can still receive data
data arrives
after close()
any data that arrives later, TCP
knows the socket is closed and
so replies as though it were never
open in the first place by sending
a RST. No automatic RST.
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";
}
this line executed:
asd
dsa
shutting down...
read failed
client code
sever code
first server send
print SESSION $msg_out;
second server send
$bytes_out += length($msg_out);
last;
}
sleep(1);
print SESSION "again: $msg_out\n"; # print a second time
close() ethereal dump:
client: port 35192
server: port 2007
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
packet 8: close first to FIN
packet 11: client sends RST
packet 10: server sends 12 more char
TCP function (cont):
$boolean = shutdown(SOCKET,$how);
•
•
•
•
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())
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";
}
this line executed:
asd
dsa
shutting down...
read: again: dsa
client code
server code
first server send
print SESSION $msg_out;
second server send
$bytes_out += length($msg_out);
last;
}
sleep(1);
print SESSION "again: $msg_out\n"; # print a second time
client: port 35188
server: port 2007
ethereal dump:
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
packet 10: server first to FIN
packet 9: client ACKs 12 char
packet 8: server sends 12 more char
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);
NOTE: 2nd server write sends and
did not cause error; BROKEN
PIPE SIGNAL arrived later.
3rd server write resulted in error
and no TCP send occurs
• 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";
}
this line executed:
asd
dsa
shutting down...
read failed
client code
server code
first server send
print SESSION $msg_out;
second server send
$bytes_out += length($msg_out);
last;
}
third server send
sleep(1);
print SESSION "again: $msg_out\n"; # print a second time
sleep(3);
print SESSION "again: $msg_out\n"; # print a second time
client: port 35205
server: port 2007
ethereal dump:
packets 1-3: 3WHS
packet 4: client sends 4 char
packet 6: server echoes 4 char
packet 8: client sends FIN
packet 10: server sends 12 char
packet 9: client ACKs 12 char
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 socket
bind(); # binds to passed port number
listen(); # listens for incoming connection requests
accept(); # accepts incoming connection request
Connection schematic:
1st
socket();
bind();
listen();
server
listening
socket
2nd
connect()
client
accept();
4th
accept
loop
3rd
established
connection
connected
socket
listening
socket
separate
file descriptor
spawns new
connected socket
blocks until
connection
request arrives
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:
$boolean = bind(SOCK,$my_addr);
• 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
Server Socket Functions 2:
$boolean = listen(SOCK,$max_queue);
• 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.
Server Socket Functions 3:
$boolean = accept(CONN_SOCK,LISTEN_SOCK);
• 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.
Server Socket Functions 4:
$my_addr = getsockname(SOCK);
$remote_addr = getpeername(SOCK);
• 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).
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.
$value = getsockopt(SOCK,$level,$option_name);
$boolean = setsockopt(SOCK,$level,$option_name,$option_value);
• $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.
Adjusting Socket Options:
• If the option value is boolean then use 1 or 0 for
$option_value.
setsockopt(SOCK,SO_SOCKET,SO_BROADCAST,1);
my $reuse = getsockopt(SOCK,SO_SOCKET,SO_BROADCAST);
• Not very useful for TCP, we don’t often broadcast using
that protocol.
• Some values passed to setsockopt() are packed binary
$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.
setsockopt(SOCK,SO_SOCKET,S_REUSEADDR,1) or die “..”;
bind(SOCK,$my_addr);
• You can have multiple processes bound to the same
socket. They compete for incoming connections. Must be
the same user.
fcntl() and ioctl() funcions:
• Alternative way to handle socket options.
• We use this in Chapter 13 (non-blocking IO).
Other socket operations:
$bytes = send(SOCK,$data,$flags[,$destination]);
•
•
•
•
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().
Other socket operations:
$bytes = recv(SOCK,$buffer,$length,$flags);
•
•
•
•
•
•
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.
Other socket operations:
$boolean = socketpair(SOCK_A,SOCK_B,$domain,$type,$protocol);
•
•
•
•
•
•
•
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.
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”).
$/ = “\015\012”; # gives you control of what to expect
• Socket exports $CRFL, $CR and $LF along with
constants CRLF, CR and LF.
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.