Chapter Outline
Sockets
What is a Socket
Socket ConnectionsSocket Attributes
Network Information
Using Sockets
Socket Addresses
Socket Communications
Host and Network Byte OrderingThe Internet Daemon
Multiple Clients
Socket Options
selectMultiple Clients
Summary
Socket ConnectionsLecture Notes
Sockets
The socket interface is an extension of pipes. Sockets are used to communicate across networks.
We will now look at:
![]()
What is a Socket?
A socket is a communication mechanism that allows client/server systems to be developed either locally, on a single machine,or across networks.
Sockets are created and used in a different way to pipes because they make a clear distinction between client and server.
![]()
Socket Connections
Think of sockets as telephone calls into a busy building. First you get routed to the right person.
![]()
Then you communicate with that person.
![]()
Try It Out - A Simple Local Client
Here's an example of a very simple socket client program, client1.c.
It shows an unnamed socket being created and connected to a server socket, called server_socket.
1. Make the necessary includes and set up the variables:
![]()
2. Create a socket for the client:
![]()
3. Name the socket, as agreed with the server:
![]()
4. Now connect our socket to the server's socket:
![]()
5. We can now read/write via sockfd:
![]()
When we run this program it fails, because we haven't yet creted the server side named socket to connect to:
![]()
Try It Out - A Simple Local Server
Here's an example of a very simple server program, server1.c, to accept connections from our client.
1. Make the necessry includes and set up the variables:
![]()
2. Remove any old socket and create an unnamed socket for the server.
![]()
3. Name the socket:
![]()
4. Crete a connection queue and wait for clients:
![]()
5. Accept a connection:
![]()
6. We can now read/write to client on client_sockfd:
![]()
When we run the server program, it creates a socket and waits for connections. It is best to run it in the background.
![]()
Now start the client program in the foreground.
As the server waits for connections, the server prints a message.
![]()
The device type is socket, shown by the s at the front of the permissions and the = at the end.
If we use the ps command, we can see the server running in the background.
![]()
When we run the client program we are more successful. Since the server socket exists, we can connect to it and communicate with the server.
![]()
The ouput from the server and client get mixed on our termnal.
![]()
Socket Attributes
A socket is characterized by three attributes--domain, type, and protocol.
Socket Domains
Domains essentially specify the networks medium that the socket communication will use.
Internet networking uses Internet Protocol (IP) and used IP addresses.
Socket Types
A socket domain may have a number of different ways of communicating.
Internet protocols provide two distinct levels of service: streams and datagrams.
![]()
Socket Protocols
Where allowed, we can select a specific protocol for a socket.
Using Sockets
Are are some implementation details and the system calls used.
Creating a Socket
The socket system call creates a socket and returns a descriptor that can be used for accessing the socket.
![]()
The sockets created is one end-point of a communication channel.
The domain parameter specifies the address family.
Domains include:
![]()
The socket parameter type specifies the communication characteristics to be used for the new socket. Legal values include:
![]()
Socket Addresses
Each socket domain requires its own address format.
For an AF_UNIX socket, the address is described by a structure, sockaddr_un, defined in the include file sys/un.h.
![]()
In the AF_INET domain, the address is specified using a struture called sockaddr_in, defined in netinet/in.h, which contains at least these members:
![]()
The IP address structure, in_addr, is defined as:
![]()
Naming a Socket
To make a socket available for use by other processes, a server program needs to give the socket a name.
For AF_INET sockets, this means associating the socket with an IP port number.
![]()
On successful completion, bind returns 0. If it fails, it will return -1 and set errno to one of the following:
![]()
There are some more values for AF_UNIX sockets:
![]()
Creating a Socket Queue
To accept incoming connections on a socket, a server program must create a queue to store pending requests. It does this using the listen system call:
![]()
Accepting Connections
Once a server program has created and named a socket, it can wait for connections to be made to the socket by using the accept system call.
![]()
If there are no connections pending on the socket's queue, accept will block until a client makes a connection.
You may change this behavior by using the O_NONBLOCK flag on the socket file descriptor, using the fcntl function like this:
![]()
Requesting Connections
Client programs connect to servers by establishing a connection between an unnamed socket and the server listen socket.
They do this by calling connect.
![]()
If it succeeds, connect returns 0, and -1 on error. Possible errors this time include:
![]()
Closing a Socket
You can terminate a socket connection at the server and client by calling close.
Socket Communications
Now we'll try to convert our program to network socket rather than a file system socket.
![]()
Try It Out - Network Client
1. Make the necessary includes and set up the variables.
![]()
2. Create a socket for the client:
![]()
3. Name the socket, as agreed with the server:
![]()
When we run this version of the client program, it fails to connect because there isn't a server running on port 9734 on this machine:
![]()
How It Works
The client program used the sockaddr_in structure from the include file netinet/in.h to specify an AF_INET address.
Try It Out - Network Server
We also need to modify the server program to wait for the connections on our chosen port number.
Here's a modified server, server2.c:
1. Make the necessary includes and set up the variables:
![]()
2. Create an unnnamed socket for the server:
![]()
3. Name the socket:
![]()
How It Works
The server program creates an AF_INET domain socket and arranges to accept connections on it. The socket is bound to our chosen port.
Host And Network Byte Ordering
When the server and clients above are run, you can see the network connections by using the netstat command.
![]()
Client and server programs must convert their internal integer representation to the networking ordering before tranmission.
They do this using functions defined in netinet/in.h.
They are:
![]()
To ensure correct byte ordering of the 16-bit port number, our server and client need to apply these functions to the port address.
The change to server3.c is:
![]()
We don't need to convert the function call inet_addr("127.0.0.1"), since inet_addr is defined to produce a result in network order.
The change to client3.c is:
![]()
Now, when we run server3 and client3, we see the correct port being used for the local connection.
![]()
Network Information
For a more general server and client program, we can use network information functions to determine addresses and ports to use.
Host database functions are declared in the interface header file netdb.h.
![]()
The structure returned by these functions must contain at least these members:
![]()
Similarly, information concerning services and associated port numbers is availble through some service information functions.
![]()
The structure servent contains at least these members:
![]()
The address list needs to be cast to the appropriate address type and converted from network ordering to a printable string, using the inet_ntoa conversion.
inet_ntoa has the following definition:
![]()
We can gather host database information about a computer by calling gethostbyname and printing the results.
![]()
Try It Out - Network Information
This program, getname.c, gets information about a host computer.
1. As usual, make the appropriate includes and declare the variables:
![]()
2. Set the host in question to the argument supplied with the getname call, or default to the user's machine:
![]()
3. Make the call to gethostbyname and report an error if no information is found:
![]()
4. Display the hostname and any aliases it may have:
![]()
5. Warn and exit if the host in question isn't an IP host:
![]()
6. Otherwise, display the IP address(es).
![]()
How It Works
The getname program calls gethostbyname to extract the host information from the host database.
![]()
When we use the hostname localname, the loopback network is given.
![]()
Try It Out - Connecting to a Standard Service
Here's a client program, getdate.c, that get the system time and date.
1. Start with the usual includes and declarations:
![]()
2. Find the host address and report an error if none is found:
![]()
3. Check that the daytime service exists on the host:
![]()
4. Create a socket:
![]()
5. Construct the address for use with connect...
![]()
6. ...then connect and get the information:
![]()
We can use getdate to get the time of day from any known host.
![]()
How It Works
When run, we specify a host to connect to. The daytime service port number is determined from the network database function getservbyname.
The Internet Daemon
The Internet Daemon, inetd, listens for connections on many port addresses at once.
When a client connects to a service, the inetd program runs the appropriate server.
Here's an extract for the inetd configuration file, /etc/inetd.conf, that is used to decide which servers to run:
![]()
Socket Options
There are many options that you can use to control the behavior of socket connections. THe setsockopt function is used to manipulate options.
![]()
Socket level options defined in sys/socket.h include:
![]()
Multiple Clients
We might need to consider the case of multiple, simultaneous clients connecting to a server.
Try It Out - A Server for Multiple Clients
Since we're creating child processess but not waiting for them to complete, we must arrange for the server to ignore SIG_CHLD signals to prevent zombie processes.
1. The program, server4.c has an additional include for the signal.h.
![]()
2. Create a connection queue, ignore child exit details and wait for clients:
![]()
3. Accept connection:
![]()
4. Fork to create a process for this client and perform a test to see whether we're the parent or the child:
![]()
5. If we're the child, we can now read/write to the client on client_sockfd.
![]()
6. Otherwise, we must be the prent and our work for this client is finsihed:
![]()
This new server handles multiple clients programs concurrently.
![]()
How It Works
The server program now creates a new child process to handle each client, so we see several srver waiting messages as the main program continues to wait for new connections.
select
The select system call allows a program to wait for input to arrive (or output to complete) on a number of low-level file descriptors at once.
The select function operates on data structures, fd_set, that are sets of open file descriptors.
A number of macros are defined for manipulating these sets:
![]()
The select function can use a timeout value to prevent indefinite blocking.
The timeout value is given using a struct timeval, defined in sys/time.h.
![]()
The select system call has the following prototype:
![]()
The select call returns the totsal number of descriptors in the modified set.
![]()
Try It Out - select
The program, select.c, illustrates the use of select.
1. Do the includes and declarations and then initialize inputs to handle input from the keyboard:
![]()
2. Wait for input on stdin for a maximum of 2.5 seconds:
![]()
3. After the wait, we test result. If there has been no input, the program loops again. If there has been an error, the program exits:
![]()
4. If there is some actiion, we read the input and echo it until end of line or Ctrl-D.
![]()
When teh rpogram runs, it prints timeout every two and a half second. Whatever we type is echoed.
![]()
How It Works
The program uses the select call to examine the state of the standard input..
Multiple Clients
We can use select to handle multiple clients simultaneously by our simple server.
Try It Out - An Improved Multiple Client/Server
1. We include sys/time.h and sys/ioctl.h headers in place of signal.h.
![]()
2. Create and name a socket for the server:
![]()
3. Create a connection queue and initialize readfds to handle input from server_sockfd:
![]()
4. Now wait for clients and requests
![]()
5. Once we know we've got activity, we find which descriptor it's on by checking each in turn using FD_ISSET:
![]()
6. If the activity is on server_sockfd, it must be a request for a new connection and we add the associated client_sockfd to the descriptor set:
![]()
7. If it isn't the server, it must be a client activity and we server it.
![]()
When we run this version of the server, it deals with multiple clients sequentially in a single process.
![]()
Socket Connections
To complete the analogy we began at the start of the chapter, this table shows the parallels which may be drawn between socket connections and a telephone exchange:
![]()
Summary
In this chapter, we've covered another method of inter-process communication: sockets. These allow us to develop client/server applications to run across networks.
CS 248 - UNIX Programming Web Site Menu
Information | Syllabus | Schedule | Online "Lectures" | Projects | Quizzes | Web Board
Copyright © 2001 by James L. Fuller, all rights reserved.