The first argument of the socket call is the domain. We have seen two domains so far, the Internet Domain Version 4 (AF-INET), and the Internet Domain Version 6 (AF_INET6). Another domain is the Unix Domain (AF_LOCAL or AF_UNIX). This sets up a socket connection between two processes on the same host. It is similar to a pipe except that it is full duplex and the two two ends of communication do not need to have a common ancestor.
It is also equivalent to using localhost in the client to connect to a server on the same machine, although the performance is often better. The X Windows system uses this.
There is no concept of a port in this domain, the address is a pathname.
struct sockaddr_un {
sa_family_t sun_family; /* AF_LOCAL or AF_UNIX */
char sun_path[104]; /* null terminated pathname */
};
Once the socket has been created and bound, it works like any
other socket, so we do not need to spend much time on it.
The code for a simple server is identical to other servers
that we have seen with these minor differences.
Note. Only those lines which are different from those
of our standard server are shown. A pathname is passed in
as an argument to the program. The pathname can be either
relative (starting at the current working directory) or absolute (starting
at the root).
#include <sys/un.h>
struct sockaddr_un server;
struct sockaddr_un from;
sock=socket(AF_LOCAL, SOCK_STREAM, 0);
server.sun_family=AF_LOCAL ;
len = sizeof(server.sun_path);
if (strlen(argv[1]) > len) error("Pathname too long");
strcpy(server.sun_path,argv[1]);
If your argument was ttt, When you do an ls -l you see ./ttt
and the permissions look like this
+srwxr-xr-x+
Here is a complete Unix domain server
Here is a complete Unix domain client that you can
use to test your server. Pass a string to each of these (use the same
string for both).
You can also use Unix domain datagram sockets
The second argument to the socket call is the socket type. We have seen two types so far, SOCK_STREAM for TCP and SOCK_DGRAM for UDP. A third type is a raw socket (SOCK_RAW). A raw socket is one that skips the Transport layer completely.
The protocol can be specified. If protocol is zero, all packets go to the socket. If protocol is specified, then only packets with that protocol are received.
There is no address structure so there is no binding and no port numbers.
To read and send data, use sendto and recvfrom (The functions for sending and reading datagrams).
Ordinary users are not permitted to use this; only users with superuser privileges are allows to create a raw socket.
You would not use a raw socket for ordinary uses. It is used for network debugging or testing or other unusual uses. You can use it to build your own IP header for example. or you can use it to read or write Internet Message Control Protocl (ICMP) packets
The ping program sends ICMP message requests
A daemon is a process that runs in background and is not associated with a controlling terminal. Typical Unix systems have 20 to 50 daemons running in background doing various administrative tasks.
The windows equivalent is a service.
Most daemons are started at system initialization. There is a system initialization script that does things like this.
They generally have superuser privileges
One of these is the cron daemon, which keeps a table of events in a file such as /etc/crontab. It wakes up once a minute and sees if anything needs to be run.
If a daemon has to output a message, it can't do it directly because it has closed stdin, stdout and stderr. Therefore, messages that would normally be written to standard output or standard error are written to the system log. There is a syslogd daemon which daemons can use for this.
Here is the function prototype
void syslog(int priority, const char *message, ... )Priority ranges from 0 (emergency LOG_EMERG) thru 7 LOG_DEBUG. The second argument works like printf, so there can be multiple args.
Here is some skeleton code for creating a daemon (modified from Unix Network Programming: The Sockets Networking API Vol 1, third edition,by W. R. Stevens, B Fenner, and A. M Rudoff, Addison Wesley, 2004)
int daemon_init(const char *pname, int facility)
{
int i;
pid_t pid;
pid = fork();
if (pid < 0) error("forking");
if (pid > 0) exit(0); // parent process terminates
if (setsid() < 0) error("setsid"); // sets a new session id
//so that shell cannot send a kill signal
signal(SIGHUP, SIG_IGN); // ignore the hangup signal
pid = fork();
if (pid > 0) exit(0);
//this guarantees that the child is not a session leader
//and so it cannot obtain a controlling terminal
chdir("/"); // change to the root directory
for(i=0;i < MAXFD;i++) close(i);
open("/dev/null",O_RDONLY);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
// This guarantees that anything written to stdout or stderr will
// not cause a seg fault.
openlog(pname, gLOG_PID, facility);
The inetd Daemon
On a typical Unix system, there could be many servers in existence, waiting for a request. Before BSD4.3 each had a process associated with it. Each daemon took a slot in the process table, but was asleep most of the time.
Examples include ftp, telnet, rlogin, finger
These all do pretty much the same thing
The solution is inetd, the internet superserver
inetd starts, makes itself a daemon, reads /etc/inetd.conf and creates a socket for all services specified in the file.
Each socket is bound appropriately. Port is determined by calling getservbyname with the service-name and the protocol fields
It listens on each socket
It calls select
Whenever a connection is received on any of the listening sockets, it wakes up, forks off a child and execs the appropriate process to handle the connection.
Possible Server Designs
Iterative server best if the response from the server is quick because there is minimal overhead.
Concurrent server with fork best if there will be few connections but each connection will do extensive reading and writing over an extended period. You have to deal with zombies.
Concurrent server with threads generally better than a concurrent server with fork, because the overhead of creating a new thread is much less than that for creating a new process.
server with select This design is best for a server which wants to listen on many sockets simultaneously but where connections are relatively rare. The obvious example is the inetd described in the previous section.
Preforking server This is a new concept, but it is worth studying because it is probably the best design for servers which have to handle many requests and response time is important. For example, file servers or web servers usually use a preforking model. These receive many requests and have to respond very quickly. The overhead associated with creating a new process or even a new thread for each request would be prohibitive if the server gets heavy use.
To do this, the server calls socket, bind and listen exactly as we have seen, and then calls fork several times to create a number of identical processes. Each of these processes then enters its infinite loop and each calls accept.
Recall that when a call to fork creates a new child process, all of the file descriptor information is duplicated. This means that process is listening on the same port.
Each process goes to sleep. What happens when a connection occurs is somewhat system dependent; here is how it works on Berkeley Unix. When a connection arrives, all N processes are awakened. This is because all have been put to sleep on the same wait channel. Exactly one of these will accept the connection (accept will return). The others will go back to sleep.
The code should be written such that each connection is handled concurrently. The process that accepted the connection will read the request and supply the response. Meanwhile, if other connections arrive, another process will accept and handle it. When a particular process completes a request, it closes the socket on which it received and sent the data, and goes back to the accept statement again.
One issue is how many processes to create. If there are too few processes for the number of connections, clients may still be forced to wait if all of the processes are busy handling other connections. However, there is some minimal overhead associated with waking up many processes, and so it is inefficient to create too many processes.
This may not work on other Unix implementations. One solution is to put a lock or some other mutual exclusion primitive around the accept statement so that only one process will be able to accept at any given instance.
The apache web server uses preforking with an additional twist. It can change the number of processes based on load. It periodically checks to see how many processes are busy. If most are busy, it creates more processes; if most are idle, it can kill some of the processes. The system administrator can set a minimum and maximum number of child processes.
Prethreaded server This works in much the same way as a preforking server, and has more or less the same advantages and disadvantages. One potential problem with this is that if a fatal exception occurs, such as a segmentation fault, it will kill the entire process including all the threads, while if it happens in a preforked server, it will kill that process, but the other processes can continue. Of course if code is well written, this should never happen.
Required Reading
Here is an article which covers much of the same material.
The next class will cover sockets in Java. If you are not familiar with Java, Read this and do the exercises before the next class.