This section describes the Unix BSD networking API for IP as in WR Stevens
``Unix Network Programming.'' This is a message based API.
It looks at a simple program using TCP and also
using UDP. Some other elements of this style of programming are mentioned.
Some foreign sites
Here is just a few of the machines that offer Internet services around the
world
archie.au 139.130.4.6
ftp.germany.eu.net 146.169.22.37
achilles.doc.ic.ac.uk 146.169.22.37
interviews.stanford.edu 36.22.0.175
kth.se 130.237.72.201
nic.switch.ch 130.59.1.40
sun.rediris.es 130.206.1.2
ftp.x.org 198.112.44.100
Byte ordering
To handle byte ordering for non-standard size integers there are conversion
functions
htonl - host to network, long int
htons - host to network, short int
ntohl - network to host, long int
ntohs - network to host, short int
Address conversion
These functions convert to and from the ``dotted'' addresses as in 137.92.11.1
to 32 bit integer addresses:
#include
#include
#include
#include
unsigned long
inet_addr(char *ptr)
char *
inet_ntoa(struct in_addr in)
(The structure in_addr has only one field which is the 32 bit IP address.)
Addresses
The address of an IP service given is using a structure
#include
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
Example:
The finger service (port 79) on machine 137.92.11.1 is given by
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(79);
addr.sin_addr.s_addr =
inet_addr("137.9.2.11.1");
Sockets
A socket is a data structure maintained by a BSD-Unix system to handle network
connections.
A socket is created using the call ``socket''. It returns an integer that is
like a file descriptor: it is an index into a table and ``reads'' and ``writes''
to the network use this ``socket file descriptor''.
#include
#include
int socket(int family,
int type,
int protocol);
Here ``family'' will be AF_INET for IP communications, ``protocol'' will be
zero, and ``type'' will depend on whether TCP or UDP is used.
Two processes wishing to communicate over a network create a socket each. These
are similar to two ends of a pipe - but the actual pipe does not yet exist.
Connection oriented (TCP)
One process (server) makes its socket known to the system using ``bind''. This
will allow other sockets to find it.
It then ``listens'' on this socket to ``accept'' any incoming messages.
The other process (client) establishes a network connection to it, and then
the two exchange messages.
As many messages as needed may be sent along this channel, in either direction.
TCP time client
Each machine runs a TCP server on port 13 that returns in readable form the
time on that particular machine. All that a client has to do is to connect
to that machine and then read the time from that machine.
Example:
If the program is compiled to ``tcptime'', find the time in various places
by
tcptime 139.130.4.6
tcptime 192.76.144.75
tcptime 146.169.22.37
TCP time server
The real time server can only be started by the system supervisor (usually
at boot time) as the time port is reserved.
To run the following code yourself,
change the time port to say 2013.
int main(int argc,
char *argv[])
{
int sockfd;
int nread;
struct sockaddr_in serv_addr,
client_addr;
time_t t;
if ((sockfd =
socket(AF_INET,
SOCK_STREAM, 0))
< 0) {
perror(NULL);
exit(2);
}
serv_addr.sin_family =
AF_INET;
serv_addr.sin_addr.s_addr =
inet_addr(INADDR_ANY);
serv_addr.sin_port =
htons(TIME_PORT);
listen(sockfd, 5);
for (;;;) {
client_sockfd =
accept(sockfd,
&client_addr,
&len);
time(&t);
sprintf(buf, "%s",
asctime(localtime(t)));
len = strlen(buf) + 1;
write(1, buf, len);
close(client_sockfd);
}
}
Connectionless (UDP)
In a connectionless protocol both sockets have to make their existence known
to the system using ``bind''. This is because each message is treated separately,
so the client has to find the server each time it sends a message and vice
versa.
When bind is called it binds to a new port - it cannot bind to one already
in use. If you specify the port as zero the system gives you a currently unused
port.
Because of this extra task on each message send, the processes do not use read/write
but recvfrom/sendto. These functions take as parameters the socket to write
to, and the address of the service on the remote machine.
Time client (UDP)
The UDP time server requires a datagram to be sent to it. It ignores the contents
of the message but uses the return address to send back a datagram containing
the time.
Socket controls
Sockets are treated by the O/S as devices and so there are a variety of device
driver controls that can be used (see later). For example, the command ``fcntl''
can be used to make a socket non-blocking, and ``select'' can be used to test
if a socket (device) has input or output pending.
In addition, ``getsockopt'' and ``setsockopt'' can be used for more specific
socket control:
broadcast - allowed for datagrams
keepalive - periodic transmits on
connected sockets
If a read or write does not return, it should timeout. What should the time
limit be? On your own machine or on your local network it should be in milliseconds.
To Melbourne in seconds, whereas to Scandinavia it should probably be minutes.
Timeout algorithms should adjust the time according to the curent trip time
in some manner. They can be implemented using timer signals.