A standard model for distributed applications is
the client-server model. A server is a process that is waiting to be contacted
by a client process so that the server can do something or the client. A
typical scenario is as follows:
A communication between two processes running on two computer systems can be
completely specified by the association: {protocol, local-address,
local-process, remote-address, remote-process} We also define a half association as either {protocol, local-address,
local-process} or {protocol, remote-address, remote-process}, which specify half
of a connection. This half association is also called socket, or transport
address. The term socket has been popularized by the Berkeley Unix networking
system, where it is "an end point of communication", which corresponds to the
definition of half association.
For a UNIX file, there are six system calls for input/output (I/O): open,
create, close, read, write and lseek. All these system calls work with a file
descriptor, a number corresponding to a certain file. It would be nice if the
interface to the network facilities maintained the file descriptor semantics of
the Unix file system, but network I/O involves more details and options than the
file input/output. However, the difference is not extremely big.
This figure shows a time line of a typical scenario that takes place for a
connection-oriented transfer. First the server is started, then sometimes later
a client is started that connects to the server.
To do network I/O, the first thing a process must do is call the socket (see
the man page for the system calls presented, e.g. man -s3n socket) system call,
specifying the type of communication protocol desired.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int family, int type, int protocol);
for our lab, we will use only the internet protocols and stream socket type, so
the function call will look like: sockfd = socket(AF_INET, SOCK_STREAM, 0); The
socket system call returns a small integer value, similar to a file descriptor.
We call this a socket descriptor, or a sockfd.
Up to now, for the association {protocol, local-address, local-process,
remote-address, remote-process} we have only specified the protocol. In order
to fill the local-address and local-process elements of the association we will
use the bind system call.
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, int addrlen);
The second argument of bind is a pointer to a protocol-specific address, and
the third argument is the size of the address structure. bind tells the system
"this is my address and any messages received for this address are to be given
to me".
Then, the server indicates that is willing to receive connections:
int listen(int sockfd, int backlog);
The backlog argument specifies how many connection requests can be queued by
the system while it waits for the server to execute the accept system call.
After executing the listen system call, an actual connection is waited for by
having the server execute the accept system call.
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *peer, int *addrlen);
accept takes the first connection request from the queue, and creates another
socket with the same proprieties as sockfd. When a connection request is
received and accepted, the new socket descriptor returned by accept refers to a
complete association {protocol, remote-address, remote-process} with the last
two fields filled with information from the client. The client's address is
also set in the second (*peer) parameter together with its length, *addrlen;
A client process connects to a socket descriptor following the socket system
call to establish a connection with a server:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *servaddr, int addrlen);
The sockfd is a socket descriptor that was returned by the socket system
call. The second and the third argument are a pointer to a socket address, and
its size.
From now on, the server and the client can use the write/read system calls
for file descriptors in order to communicate. Stream sockets exhibit a
behaviour with the read and write system calls that differs from normal file
I/O. A read or a write on a socket might input or output fewer bytes than
requested, but this is not an error condition. The reason is that buffer limits
might be reached for the socket in the kernel and all that is required is for
the caller to invoke the read or write system call again, for the remaining
bytes.
Many of the socket system calls require a pointer to a socket address
structure as an argument. The definition of this structure is in
<sys/socket.h>:
struct sockaddr {
u_short sa_family; /* address_family: AF_xxx value */
char sa_data[14];
};
The contents of the 14 bytes of protocol-specific address are interpreted
according to the type of address. For the Internet family, the following
structures are defined in <netinet/in.h>:
struct in_addr {
u_long s_addr; /* 32 bit netid/hostid */
};
struct sockaddr_in {
short sin_family; /* AF_INET */
u_short sin_port; /* 16 bit port number, network byte ordered */
srtuct in_addr sin_addr; /* 32 bit netid/hostid, network byte ordered */
char sin_zero[8]; /* unused */
}
Both the client and the server close their sockets using the close function
call.
It looks like you're new here. If you want to get involved, click one of these buttons!