1. What is Server Applications?

Server Applications:

A system offers a service by having an application running that is listening at the service port and willing to accept a connection from a client. If there is no application listening at the service port then the machine doesn't offer that service.

The SMTP service is provided by an application listening on port 25. On Unix systems this is usually the sendmail(1M) application which is started at boot time.

[2:20pm julian] ps -agx | grep sendmail
419 ? SW 0:03 /usr/lib/sendmail -bd -q15m
18438 ? IW 0:01 /usr/lib/sendmail -bd -q15m

[2:28pm julian] netstat -a | grep smtp
tcp 0 0 julian.3155 acad3.alask.smtp SYN_SENT
tcp 0 0 *.smtp *.* LISTEN

In the example we have a process listening to the smtp port (for inbound mail) and another process talking to the smtp port on acad3.alaska.edu (ie. sending mail to that system).

2. What is Client Connect?

A client application creates a socket(3N)
and then issues a connect(3N)
to a service specified
in a sockaddr_in structure:

int tcpopen(host,service)
char *service, *host;
{ int unit;
struct sockaddr_in sin;
struct servent *sp;
struct hostent *hp;

if ((sp=getservbyname(service,"tcp")) == NULL)
then error...
if ((hp=gethostbyname(host)) == NULL)
then Ierror...
bzero((char *)&sin, sizeof(sin))
etc...
if ((unit=socket(AF_INET,SOCK_STREAM,0)) < 0)
then error...
if (connect(unit,&sin,sizeof(sin)) <0)
then error...
return(unit);
}

The result returned is a file descriptor which is connected to a server process. A communications channel on which one can conduct an application specific protocol.

Client Communication:

Having connected a socket to a server to establish a file descriptor communication is with the usual Unix I/O calls. You have Inter Process Communication (or IPC) to a server.

Many programmers turn file descriptors into stdio(3S) streams so they can use fputs, fgets, fprintf, etc. -- use fdopen(3S).

main(argc,argv)
int argc;
char *argv[];
{
int unit,i;
char buf[BUFSIZ];
FILE *sockin,*sockout;

if ((unit=tcpopen(WHOHOST,WHOPORT))
< 0) then error...
sockin=fdopen(unit,"r");
sockout=fdopen(unit,"w");
etc...
fprintf(sockout,"%sn",argv[i]);
etc...
while (fgets(buf,BUFSIZ,sockin)) etc...

Stdio Buffers:

Stdio streams have powerful manipulation tools (eg. fscanf is amazing). But beware, streams are buffered! This means a well placed fflush(3S) is often required to flush a buffer to the peer.

fprintf(sockout,"%sn",argv[i]);
fflush(sockout);

while (fgets(buf,BUFSIZ,sockin)) etc...

Many client/server protocols are client driven -- the client sends a command and expects an answer. The server won't see the command if the client doesn't flush the output. Likewise, the client won't see the answer if the server doesn't flush it's output.

Watch out for client and server blocking -- both waiting for input from the other.

3. What is File Descriptors and Sockets?

File Descriptors:

File Descriptors are the fundamental I/O object. You read(2) and write(2) to file descriptors.

int cc, fd, nbytes;
char *buf;

cc = read(fd, buf, nbytes);
cc = write(fd, buf, nbytes)

The read attempts to read nbytes of data from the object referenced by the file descriptor fd into the buffer pointed to by buf. The write does a write to the file descriptor from the buffer. Unix I/O is a byte stream.

File descriptors are numbers used for I/O. Usually the result of open(2) and creat(2) calls.

All Unix applications run with stdin as file descriptor 0, stdout as file descriptor 1, and stderr as file descriptior 3. But stdin is a FILE (see stdio(3S)) not a file descriptor. If you want a stdio FILE on a file descriptor use fdopen(3S).

Sockets:

A Socket is a Unix file descriptor created by the socket(3N) call -- you don't open(2) or creat(2) a socket. By way of comparison pipe(2) creates file descriptors too -- you might be familiar with pipes which predate sockets in the development of the Unix system.

int s, domain, type, protocol;
s = socket(domain, type, protocol);
etc...
cc = read(s, buf, nbytes);

The domain parameter specifies a communications domain (or address family). For IP use AF_INET but note that socket.h lists all sorts of address families. This is to inform the system how an address should be understood -- on different networks, like AF_DECnet, addressing may be longer than the four octets of an IP number. We're only concerned with IP and the AF_INET address family.

The type parameter specifies the semantics of communication (sometimes know as a specification of quality of services). For TCP/IP use SOCK_STREAM (for UDP/IP use SOCK_DGRAM). Note that any address family might support those service types. See socket.h for a list of service types that might be supported.

A SOCK_STREAM is a sequenced, reliable, two-way connection based byte stream. If a data cannot be successfully transmitted within a reasonable length of time the connection is considered broken and I/O calls will indicate an error.

The protocol specifies a particular protocol to be used with the socket -- for TCP/IP use 0. Actually there's another programmers interface getprotobyname(3N) that provides translates protocol names to numbers. It's an interface to the data found in /etc/protocols -- compare with the translation of service names to port numbers discussed above.

4. What is Socket Addressing?

A Socket Address is a host.port pair (communication is between host.port pairs -- one on the server, the other on the client). We know how to determine host numbers and service numbers so we're well on our way to filling out a structure were we specify those numbers. The structure is sockaddr_in, which has the address family is AF_INET as in this fragment:

int tcpopen(host,service)
char *service, *host;
{ int unit;
struct sockaddr_in sin;
struct servent *sp;
struct hostent *hp;
etc...
if ((sp=getservbyname(service,"tcp"))
== NULL) then error...
if ((hp=gethostbyname(host)) == NULL)
then error...

bzero((char *)&sin, sizeof(sin));
sin.sin_family=AF_INET;
bcopy(hp->h_addr,(char *)&sin.sin_addr,
hp->h_length);
sin.sin_port=sp->s_port;
etc...

The code fragment is filling in the IP address type AF_INET, port number and IP address in the Socket Address structure -- the address of the remote host.port where we want to connect to find a service.

There's a generic Socket Address structure, a sockaddr, used for communication in arbitrary domains. It has an address family field and an address (or data) field:

/* from: /usr/include/sys/socket.h */


struct sockaddr {
u_short sa_family; /*address family */
char sa_data[14];/*max 14 byte addr*/
};


The sockaddr_in structure is for
Internet Socket Addresses
address family AF_INET). An instance
of the generic socket address.

/* from: /usr/include/netinet/in.h */
struct sockaddr_in {
short sin_family; /* AF_INET */
u_short sin_port; /* service port */
struct in_addr sin_addr; /*host number */
char sin_zero[8]; /* not used */
};


The family defines the interpretation of the data. In other domains addressing will be different -- services in the UNIX domain are names (eg. /dev/printer). In the sockaddr_in structure we've got fields to specify a port and a host IP number (and 8 octets that aren't used at all!). That structure specifies one end of an IPC connection. Creating that structure and filling in the right numbers has been pretty easy so far.

5. How do we get a process bound behind a port?

Server Bind:

A Server uses bind(3N) to establish the local host.port assignment -- ie. so it is the process behind that port. That's really only required for servers -- applications which accept(3N) connections to provide a service.

struct servent *sp;
struct sockaddr_in sin;

if ((sp=getservbyname(service,"tcp")) == NULL) then error...

sin.sin_family=AF_INET;
sin.sin_port=sp->s_port;
sin.sin_addr.s_addr=htonl(INADDR_ANY);

if ((s=socket(AF_INET,SOCK_STREAM,0)) < 0) then error...
if (bind(s, &sin, sizeof(sin)) < 0) then error...

htonl(3N) converts a long to the right sequence (given different byte ordering on different machines). The IP address INADDR_ANY means all interfaces. You could, if you wanted, provide a service only on some interfaces -- eg. if you only provided the service on the loopback interface (127.0.0.1) then the service would only be available to clients on the same system.

What this code fragment does is specify a local interface and port (into the sin structure). The process is bound to that port -- it's now the process behind the local port.

Client applications usually aren't concerned about the local host.port assignment (the connect(3N) does a bind o some random but unused local port on the right interface). But rcp(1) and related programs (like rlogin(1) and rsh(1)) do connect from reserved port numbers.

For example, the version of tcpopen.c used in the Passwdd/Passwd -- An authentication Daemon/Client. There's an instance where a client application connects from a reserved port.

Listen and Accept:

To accept connections, a socket is created with socket(3N), it's bound to a service port with bind(3N), a queue for incoming connections is specified with listen(3N) and then the connections are accepted with accept(3N) as in this fragment:

struct servent *sp;
struct sockaddr_in sin,from;

if ((sp=getservbyname(service,"tcp")) == NULL)
then error...
sin.sin_family=etc...
if ((s=socket(AF_INET,SOCK_STREAM,0)) < 0)
then error...
if (bind(s, &sin, sizeof(sin)) < 0)
then error...
if (listen(s,QUELEN) < 0) then error...
for (;;) {
if ((g=accept(f,&from,&len)) < 0)
then error...
if (!fork()) {
child handles request...
...and exits
exit(0);
}
close(g); /* parent releases file */
}


This is the programming schema used by utilities like sendmail(1M) and others -- they create their socket and listen for connections. When connections are made, the process forks off a child to handle that service request and the parent process continues to listen for and accept further service requests.

6. What is Inetd Services?

Not all services are started at boot time by running a server application. Eg. you won't usually see a process running for the finger service like you do for the smtp service. Many are handled by the InterNet Daemon inetd(1M). This is a generic service configured by the file inetd.conf(4).

[2:35pm julian] page /etc/inetd.conf
# $Author: reggers $
# $Date: 1997/05/02 20:17:16 $
#
# Internet server configuration database
ftp stream tcp nowait root /usr/etc/ftpd ftpd
telnet stream tcp nowait root /usr/etc/telnetd telnetd
shell stream tcp nowait root /usr/etc/rshd rshd
login stream tcp nowait root /usr/etc/rlogind rlogind
exec stream tcp nowait root /usr/etc/rexecd rexecd
uucpd stream tcp nowait root /usr/etc/uucpd uucpd
finger stream tcp nowait nobody /usr/etc/fingerd fingerd
etc...
whois stream tcp nowait nobody /usr/lib/whois/whoisd whoisd
etc...


Inetd Comments:

For each service listed in /etc/inetd.conf the inetd(1M) process, and that is a process is started at boot time, executes the socket(3N), bind(3N), listen(3N) and accept(3N) calls as discussed above. Inetd also handles many of the daemon issues (signal handling, set process group and controlling tty) which we've studiously avoided.

The inetd(1M) process spawns the appropriate server application (with fork(2) and exec(2)) when a client connects to the service port. The daemon continues to listen for further connections to the service while the spawned child process handles the request which just came in.

The server application (ie. the child spawned by inetd(1M)) is started with stdin and stdout connected to the remote host.port of the client process which made the connection. Any input/output by the server appliation on stdin/stdout are sent/received by the client application. You have Inter Process Communication (or IPC)!

This means, any application written to use stdin/stdout can be a server application. Writing a server application should therefore be fairly simple.

7. What is Whois Daemon?

Simple WHOIS Daemon, provides the standard Internet whois directory service It is much simpler to setup and administrator than the other whois daemons available, such as RIPE whois or the original SRC whois. This is because it uses a flat-text file, /etc/swhoisd.conf, instead of a complex database. This whois server is recommended only for small databases (preferably under 100 records and no more than 1000).

8. How to run the WHOIS Daemon?

You can run the whois daemon (on the server) to
see what it does:

[3:27pm julian] echo reggers | /usr/lib/whois/whoisd
There were 1 matches on your request.

Full Name: Quinton, Reg
Department: Info Tech Svcs
Room: NSC 214
Phone: 679-2111x(6026)
Index Key: 481800
Machine Address: reggers@julian.uuu.com
Directory Addresses: reg.quinton@uuu.com
: r.quinton@uuu.com
: reggers@uuu.com
: quinton@uuu.com

For more information try 'whois help'.

The program is command driven -- you give a command (or query string) on stdin, it produces results on stdout, and exits.

Connecting to the Server:

You can make a telnet(1) connection to
the whois service on the server.

[3:47pm julian] telnet julian whois
Trying 129.100.2.12 ... Connected to julian.
Escape character is '^]'.
reggers .... my command input
There were 1 matches on your request.

Full Name: Quinton, Reg
Department: Info Tech Svcs
Room: NSC 214
Phone: 679-2111x(6026)
Index Key: 481800
Machine Address: reggers@julian.uuu.com
Directory Addresses: reg.quinton@uuu.com
: r.quinton@uuu.com
: reggers@uuu.com
: quinton@uuu.com

For more information try 'whois help'.
Connection closed by foreign host.

9. What is Whois Client?

Whois Client: This is whois, a very simple and generic whois client. This client, unlike the classic whois client, does not check for supported flags at the client side, except for -h (whois host) and -p (whois port).

The whois(1) client makes a TCP/IP connection
to the server and conducts the kind of protocol
that you would type if you where to make
a connection by hand:


[7:30am julian] whois reggers
There were 1 matches on your request.

Full Name: Quinton, Reg
Department: Info Tech Svcs
Room: NSC 214
Phone: 679-2111x(6026)
Index Key: 481800
Machine Address: reggers@julian.uuu.com
Directory Addresses: reg.quinton@uuu.com
: r.quinton@uuu.com
: reggers@uuu.com
: quinton@uuu.com

For more information try 'whois help'.

The client sends the command "reggers", the server sends back the answer and the client displays the answer received to the user. When the server is finished the connection is closed.

Sample code: whois(1) client


sub tcpopen {
use Socket; # need socket interface
my($server, $service) = @_;# args to this function
my($proto, $port, $iaddr); # local variables
my($handle)="$server::$service";
# localized obscure handle

die("550:Cannot getprotobyname('tcp')
")
unless ($proto = getprotobyname('tcp'));

die("550:Cannot getservbyname($service)
")
unless ($port = getservbyname($service, 'tcp'));

die("550:Cannot gethostbyname($server)
")
unless ($iaddr = gethostbyname($server));

die("550:Cannot create socket
")
unless socket($handle, PF_INET, SOCK_STREAM, $proto);

die("550:Cannot connect($service://$server)
")
unless connect($handle, sockaddr_in($port, $iaddr));

# unbuffered I/O to that service

select($handle); $| = 1; select(STDOUT); $| = 1;

return($handle);
}

10. What are basic functions of network programming interface?

Most operating systems provide precompiled programs that communicate across a network. Common examples into the TCP/IP world are web clients(browsers) and web servers, and the FTP and TELNET clients and servers.

A socket is an endpoint used by a process for bi-directional communication with a socket associated with another process. Sockets, introduced in Berkeley Unix, are a basic mechanism for IPC on a computer system, or on different computer systems connected by local or wide area networks(resource 2). To understand some structs into this subject is necessary a deeper knowledge about the operating system and his networking protocols. This subject can be used as either beginners programmers or as a reference for experienced programmers.

The Socket Function
Most network applications can be divided into two pieces: a client and a server.

Creating a socket
#include <sys/types.h>
#include <sys/socket.h>

When you create a socket there are three main parameters that you have to specify:

* the domain
* the type
* the protocol

int socket(int domain, int type, int protocol);

The Domain parameter specifies a communications domain within which communication will take place, in the example the domain parameter was AF_INET, that specify the ARPA Internet Protocols The Type parameter specifies the semantics of communication, in the mini chat used the Stream socket type(SOCK_STREAM), because it offers a bi-directional, reliable, two-way connection based byte stream(resource 2). Finally the protocol type, since used a Stream Socket type, must use a protocol that provide a connection-oriented protocol, like IP, decide to use IP in our protocol Type, and we saw in /etc/protocols the number of ip, 0. So the function now is:

s = socket(AF_INET , SOCK_STREAM , 0)

where 's' is the file descriptor returned by the socket function.

Since the mini chat is divided in two parts it will be divided the explanation in the server, the client and the both, showing the basic differences between them, as see next.

The Mini-chat Server structure
Binding a socket to a port and waiting for the connections

Like all services in a Network TCP/IP based, the sockets are always associated with a port, like Telnet is associated to Port 23, FTP to 21... In the Server, to do the same thing, bind some port to be prepared to listening for connections ( that is the basic difference between Client and Server), Listing 2. Bind is used to specify for a socket the protocol port number where it will be waiting for messages.

So there is a question, which port could be new service? Since the system pre-defined a lot of ports between 1 and 7000 ( /etc/services ) choose the port number 15000.

The function of bind is:


int bind(int s, struct sockaddr *addr, int addrlen)

The struct necessary to make socket works is the struct sockaddr_in address; and the follow lines to say to system the information about the socket.

The type of socket
address.sin_family = AF_INET /* use a internet domain */
The IP used
address.sin_addr.s_addr = INADDR_ANY /*use a specific IP of host*/
The port used
address.sin_port = htons(15000); /* use a specific port number */

And finally bind our port to the socket

bind(create_socket , (struct sockaddr *)&address,sizeof(address));

Now another important phase, prepare a socket to accept messages from clients, the listen function is used on the server in the case of connection oriented communication and also the maximum number of pending connections(resource 3).

listen (create_socket, MAXNUMBER)

where MAXNUMER in the case is 3. And to finish, tell the server to accept a connection, using the accept() function. Accept is used with connection based sockets such as streams.

accept(create_socket,(struct sockaddr *)&address,&addrlen);

As see in Listing 2 The parameters are the socket descriptor of the master socket (create_socket), followed by a sockeaddr_in structure and the size of the structure.(resource 3)

The Mini-chat Client structure


Maybe the biggest difference is that client needs a Connect() function. The connect operation is used on the client side to identify and, possibly, start the connection to the server. The connect syntax is

connect(create_socket,(struct sockaddr *)&address,sizeof(address)) ;

The common structure

A common structure between Client and the Server is the use of the struct hostent as seeing in Listing 1 and 2. The use of the Send and Recv functions are another common codes.

The Send() function is used to send the buffer to the server

send(new_socket,buffer,bufsize,0);

and the Recv() function is used to receive the buffer from the server, look that it is used both in server and client.

recv(new_socket,buffer,bufsize,0);

Since the software of the TCP/IP protocol is inside the operating system, the exactly interface between an application and the TCP/IP protocols depends of the details of the operating system(resource 4). In the case, examine the UNIX BSD socket interface because Linux follow this. The Mini-chat developed here is nothing more than a explain model of a client/server application using sockets in Linux and should be used like a introduction of how easy is to develop applications using sockets. After understand this you can easily start to think about IPC (interprocess Communication), fork, threads(resource 5) and much more. The basic steps to make it work is:

1. Run the server
2. Run the client with the address of the server
By Pedro Paulo Ferreira Bueno and Antonio Pires de Castro Junior

Download Interview PDF