Transcript Sockets
Sockets
CIS 370
Lab 10
UMass Dartmouth
Introduction
Sockets provide a simple programming
interface which is consistent for processes
on the same machine or across different
machines.
The aim of UNIX sockets is to provide a
means of IPC that is sufficiently generic to
allow bi-directional messages between two
processes irrespective of where they reside.
General Information
For any communication to take place
between processes, the machines on which
they are running must be connected.
– Hardware level: network equipment such as
cables, cards and devices such as routers.
– Software level: by a standard set of network
protocols.
Simplest IPC model is client-server.
– Server is a process which provides a service.
– Client is a process (possibly on another host)
that connects to the server to invoke its service.
Types of Communication
Processes that send messages across a
network can choose one of two ways to
communicate.
– The connection-oriented model (virtual circuit).
• Uses TCP – CIS475 (Computer Networks).
– The connectionless-oriented model.
• Uses UDP – CIS475 (Computer Networks).
The Connection-Oriented Model
The connection-oriented model can be used
by a process which needs to send an
unformatted, uninterrupted stream of
characters to the same constant destination.
For example: a remote login connection
where a client system has a virtual
connection to a server.
Like a phone network.
The Connectionless Model
Suppose a server wants to send a broadcast
message to all its clients (and is not
necessarily concerned that the clients get
the message), the server can use a
connectionless-oriented model.
The process sends the message to a
specified network address, it then sends the
next message to a different address.
Like United States Postal Service.
Addressing
When processes are communicating across
a network they must know the network
address of the machine that the processes
are residing on.
The address essentially gives the physical
location of a machine on a network.
Addresses are generally layered,
representing the different levels of a
network.
IP Addressing
– Across the world’s networks there is an almost
universally accepted addressing standard - IP
internet addressing.
– An IPv4 address consists of four decimals
separated by periods.
• 134.88.14.211 - uranium’s (current) IP address.
– At the programming level, IP addresses are
stored in an in_addr_t type.
IP Addressing
UNIX provides the inet_addr() system call
that converts the four-decimal representation
to the in_addr_t representation.
Usage
#include <arpa/inet.h>
in_addr_t inet_addr(const char *ip_address);
is in the form discussed earlier,
the routine returns an address in the form of
in_addr_t (a -1 if in error).
*ip_address
Ports
Once the address of the machine to access is
known, the client process also needs to
know the correct server processes to
connect to.
Each server process sets and listens for
connections on a specified port number.
Therefore a client process will ask for a
connection to a specific machine and port.
Ports
At the programming level, ports are stored as an
in_port_t type.
UNIX systems expect a port to be a 16-byte
unsigned int.
– Our lab machines are 32-bit systems, so
integers are 4 bytes. Need to convert!
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
Ex: in_port_t serverPortNum = htons(7000);
Sockets
In all forms of communication, both the
client and server must establish their own
transport end points, or sockets.
– Like a wall socket: can’t get electricity to a
lamp’s plug without a socket!
These are handles used to establish a link
between processes across a network.
Different types of sockets are used for
different types of IPC within the same
machine or on different machines.
Sockets
A specific form of socket for network
communication is shown below:
#include <netinet/in.h>
struct sockaddr_in{
sa_family_t
sin_family;
in_port_t
sin_port;
struct in_addr sin_addr;
}
// internet address family
// port number
// holds the IP address
Creation of sockets is achieved using the
socket() system call.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain
tells the call where the socket is to
be used.
– AF_INET - internet domain.
– AF_UNIX - same UNIX machine.
type specifies connection
– SOCK_STREAM - TCP.
– SOCK_DGRAM - UDP.
protocol indicates type
– SOCK_STREAM - TCP.
– SOCK_DGRAM - UDP.
type.
of communication.
Returns a file descriptor for the socket.
Server-Side Binding
The bind() system call associates the true network
address of a machine with a socket identifier.
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *address,
size_t add_len);
sockfd is the socket file descriptor, returned by the
socket() system call.
*address is a pointer to a socket address structure.
add_len is the size of the socket structure referenced by
address.
Returns 0 on success, -1 on failure.
Server-Side Listening
After binding and before any client system can
connect to the newly created server endpoint, the
server must set itself up to wait for connections.
#include <sys/sockets.h>
int listen (int sockfd, int queue_size);
sockfd is the socket file descriptor.
queue_size is the number of incoming
connection requests that the server can queue.
Server-Side Accepting
When the server receives a connect()
request from a client (discussed later) it has
to create an entirely new socket to handle
the specific communication.
The first socket is used only to establish a
connection. Data isn’t passed through it.
Creation of a second socket is done using
the accept() system call.
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *address,
size_t *add_len);
sockfd
is the original socket file descriptor.
*address is a pointer to a socket address
structure. Since we already have this info
from the original socket, can set to NULL.
add_len holds the size of the socket
structure referenced by address. Can also be
NULL.
Returns a file descriptor to the new socket to
be used for communication (-1 on failure).
A Simple Example
We will use all the system calls we have
seen so far.
Server doesn’t actually do anything yet
except establish itself on a socket and wait
for a client connection.
Client-Side Connecting to Server
To request a connection to a server process
and machine, the client uses the connect()
system call.
Once the server receives this request, it will
reply with its own accept() request.
#include <sys/types.h>
#include <sys/socket.h>
int connect(int c_sockfd, const struct sockaddr
*address, size_t add_len);
c_sockfd is the client’s socket file descriptor.
*address is a pointer to a socket address structure. This
should contain the IP address and port number of the server
to connect to.
add_len holds the size of the socket structure referenced
by address.
Returns 0 on successful connection to server, -1 on failure.
The Other Half of the Example
Client simply sends a connection request to
the server.
– The client MUST know the internet address of
the server in order to connect!
Sending and Receiving Data
Now a connection has been established
between the client and the server. We’re
finally ready to communicate for IPC!
Remember, sockets have file descriptors
associated with them.
– Hopefully you recall from earlier labs that we can
read() and write() with file descriptors.
If extra options are needed, two new system
calls can be used instead.
– send() and recv().
send()
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buffer,
size_t length, int flags);
sockfd
is the socket file descriptor.
*buffer contains the message to send.
length is the size of the buffer.
flags are optional parameters. We aren’t
doing anything fancy, so these can be 0.
– This causes the behavior of send() to be
identical to write()!
recv()
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buffer,
size_t length, int flags);
sockfd
is the socket file descriptor.
*buffer contains the destination of the
received message.
length is the size of buffer.
flags can be 0.
– This causes the behavior of recv() to be
identical to read()!
Continuing Our Example
Let’s look at a complete example.
– Client accepts a lowercase character from user
at command line.
– Client sends lowercase letter to server.
– Server will convert the letter to uppercase and
send it back to client.
– Client will then output the new character to
user.
*Note that the user does not interact directly with
the server!
Closing the Connection
It is important to deal with closing the
socket connection.
Since a socket is a two-way communication
mechanism, it is impossible to predict
whether a process will be trying to read or
write when a break in the communication
occurs.
Closing the Connection
If a process attempts to write() or send() data to
a socket which has become disconnected it will
receive the SIGPIPE signal, which can be dealt with
through a signal handler.
If read() or recv() returns zero, then the
connection has been closed by the other side.
If using the connection-oriented model, the socket
close is blocked until the current data transfer is
complete.
If the connectionless model is in use, the socket is
closed immediately.
Completing Our Example
Client and server both close their sockets if
the other process dies.
For completeness, some things have been
changed from previous examples.
– Client supports multiple input characters.
– Server now supports multiple clients.
Following complete samples are available
online:
www.cis.umassd.edu/~jplante/cis370/lab10/samples/server.c
www.cis.umassd.edu/~jplante/cis370/lab10/samples/client.c
The Connectionless Model
Up to this point, we’ve looked only at the
connection-oriented model.
The remaining slides are for educational
purposes only and are optional.
The following slides are NOT necessary for
completing today’s assignment!
The Connectionless Model
Unlike the connection-oriented approach,
packets transmitted between the client and
server will arrive at their destination in an
indeterminate order.
A process wishing to send or receive
messages across a network must create its
own local socket and bind() its own
network address to that socket.
– This means clients need to bind() too!
Sending and Receiving Messages
In the connectionless model, the processes
aren’t directly connected to eachother.
Processes can send/recv data from any
process on any machine.
– This is how the Internet works!
Processes don’t automatically know who is
sending messages to them.
– Cannot use send() and recv()!
ssize_t sendto(int sockfd, const void *message,
size_t length, int flags, struct sockaddr
*send_addr, size_t *add_len);
sockfd
is the socket file descriptor.
*message contains the data to send.
length is the size of message.
flags can be 0.
*send_addr contains the information of the
recipient including its IP address and port
number.
*add_len is the size of send_addr.
ssize_t recvfrom(int sockfd, void *message, size_t
length, int flags, struct sockaddr
*send_addr, size_t *add_len);
sockfd
is the socket file descriptor.
*message is the buffer for received data.
length is the size of message.
flags can be 0.
*send_addr contains the information of the
sender including its IP address and port
number.
*add_len is the size of send_addr.
The Difference?
In both models the server has to create a
socket and bind its local address to that
socket.
In the connection model the server must
then start to listen for the incoming
connection. This step is not necessary in the
connectionless model as the client will do
more of the work.
The Difference?
From a client perspective, in the connection
model, the client just connects to the server.
In the connectionless model, the client must
create a socket and bind its local address to
that socket.
The Difference?
Different system calls are normally used to
transmit data.
The sendto() and recvfrom() calls
are normally used in the connectionless
model so that the server can retrieve
information about the sender of the message
and reply accordingly.