The client-server model is used in may type sof network communication including mail, ftp, telnet, rlogin, http, and nfs.
In this model, the server waits for requests and the client makes requests for service from the server.
Figure 12.1 (page 433): Multiple clients write requests to a shared one-way communication channel. (Modified to show multiple requests.)
Clients and servers differentiate communication channels using port numbers. A communication endpoint is specified by a host address and a port number. Standard servers use well-known port numbers that allow clients to request services. For example:
Figure 12.2 (page 434, modified): In a connectionless protocol, the server has a well-known port for client requests, but client-specific ports for responses.
In connection-oriented communication, the client sets up a connection using the server's well-known port number and then communicates over a private communications channel as shown in Figure 12.3.
Figure 12.3 (page 434, modified): In a connection-oriented protocol, the server waits for a client request by monitoring a communication endpoint whose address is known to the clients. When a client request arrives, the server creates a new communication endpoint to handle two-way symmetric communication with the client.
If the server needs to handle several concurrent requests, it can do so by forking a chaild to handle each request that comes in.
Figure 12.4 (page 436, modified): The parent-server strategy for client-serveri connection-oriented communication.
UICI: A simple connection-oriented client-server implementation
The server does the following:
UICI Prototype | Description |
int u_open(u_port_t port) |
Creates a TCP socket bound to port. Returns a file descriptor for the socket. Returns a negative value on error. |
int u_accept(int fd, char *hostn, int hostnsize) |
Waits for connection request on fd. Returns communication file descriptor. On return hostn is first hostnsize-1 characters of the client's host name. Returns a negative value on error. |
int u_connect(u_port_t port, char *hostn) |
Initiates connection to server on port port and host hostn. Returns communication file descriptor. Returns a negative value on error. |
char *u_strerror(int errnum) |
Returns a string corresponding to an error returned by u_open, u_accept or u_connect |
ssize_t u_read(int fd, void *buf, size_t nbyte) |
Reads up to nbyte bytes from fd into
buf. Returns -1 on error or the number of bytes actually read. |
ssize_t u_write(int fd, void *buf, size_t nbyte) |
Writes nbyte bytes from buf to fd. Returns -1 on error or nbyte bytes on success. |
u_read and u_write are just like read and write except that they automatically restart if interrupted by a signal and
u_write keeps writing bytes until the requested number of bytes
have been written.
These can be used with any open file descriptor.
The following figure (not from the book) shows the interaction between a
simple UICI server and client.
The server handles a single request with a single response.
The interaction of a UICI client and server.
The copyfile function can be used to copy from one open file descriptor to another. It is similar to the copy_from_network_to_file function of Program 12.1 of the text, but it is simpler because it uses u_read and u_write.
Look at serial and parallel server.
Look at client.
Bidirectional communication: copy2files
Bidirectional client and server: client2 and server2
Network Communication in General
The ISO OSI model: International Standards Organization Open Systems Interconnection reference model.
Figure 12.6, page 446: The ISO model.
Socket Implementation of UICI
The following table shows the socket function needed to implement each
UICI function.
UICI | Sockets |
u_open | socket bind listen |
u_accept | accept |
u_connect | socket connect |
u_read | read |
u_write | write |
All socket system calls return -1 and set errno.
socket | creates a file descriptor |
bind | associates a file descriptor with a port and machine |
listen | sets up buffers |
accept | waits for a connection request |
connect | requests a connection to a server |
#include <sys/types.h> #include <sys/sicket.h> int socket(int domain, int type, int protocol); int bind(int s, const struct sockaddr *address, size_t address_len); int listen(int s, int backlog); int accept(int s, struct sockaddr *address, int *address_len);
socket:
domain is AF_UNIX orAF_INET
type is SOCK_STREAM for connection-oriented TCP or SOCK_DGRAM for connectionless UDP
protocol is usually 0
bind:
s is the file descriptor returned by socket
address contains info about the family, port and machine
address_len is the size of the structure used for the address
The format of the address is determined by the address family (domain).
For AF_INET it is a struct sickaddr_in which is defined as
follows:
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; };
listen:
This is used to set the number of unaccepted outstanding requests.
We can now look at the implementation of u_open.
accept:
This waits for a connection request.
s: from the socket call.
address: filled in with information about the client making a
connection request.
address_len: You fill this in with the size of the address
buffer you provided.
To convert the info in address to a machine name you can use:
struct hostent *gethostbyaddr(const void *addr, size_t len, int type);
We can now look at the implementations of u_accept and u_connect.
We can also look at u_read and u_write.