380 likes | 571 Views
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
E N D
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-way handshake and waits for completion sends FIN flag from this 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 andbuilt 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): 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!! client code 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"; } sever code first server send this line executed: asd dsa shutting down... read failed print SESSION $msg_out; $bytes_out += length($msg_out); last; } sleep(1); print SESSION "again: $msg_out\n"; # print a second time second server send
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 10: server sends 12 more char packet 11: client sends RST
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!! client code 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"; } server code first server send this line executed: asd dsa shutting down... read: again: dsa print SESSION $msg_out; $bytes_out += length($msg_out); last; } sleep(1); print SESSION "again: $msg_out\n"; # print a second time second server send
ethereal dump: client: port 35188 server: port 2007 packets 1-3: 3WHS packet 4: client sends 4 char packet 6: server echoes 4 char packet 10: server first to FIN packet 8: server sends 12 more char packet 9: client ACKs 12 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. client code 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"; } server code first server send this line executed: asd dsa shutting down... read failed 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 second server send third server send
ethereal dump: client: port 35205 server: port 2007 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 loop accept(); 4th 3rd established connection connected socket listening socket separate file descriptor blocks until connection request arrives spawns new connected 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: $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. • $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: 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: $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”). • 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.