You are on page 1of 37

Design choices

Iterative vs Concurrent
Based on service concurrency
Connection-oriented vs connectionless
Based on transport protocols: TCP or UDP
4 types of servers in design
Iterative, connectionless server
Iterative, connection-oriented server
Concurrent, connectionless server
Concurrent, connection-oriented server
(1) Iterative
Connection-oriented
(2) Iterative
Connectionless
(3) Concurrent
Connection-oriented
(4) Concurrent
Connectionless
Connectionless (UDP)
Simple
fast
Concurrent
Multi-user
Multi-processor, multi-process, multi-
thread, async I/O
Performance
Connection (TCP)
Reliable pipe
Prone to attack
3-way handshaking (SYN attack)
Nothing going on when idle
Resource exhaustion
Client crashunintentional or intentional
Memory leak
Iterative
Simple
most app
diagnostic
programs
Iterative servers
Handle one request at a time
Easy to implement
Poor performance
Concurrent servers
Handle multiple requests concurrently at one time
from clients perspective
Regardless of single or multiple threads of execution
Complicated to implement
Better performance
Apparent concurrency
Implemented with a single thread and select()
system call
Acceptable only if the total load of requests does
not exceed its handling capacity
Good if switching between threads costs high and
data needs to be shared between connections
Real concurrency
Multi-threaded implementation
Within single process
Single thread, multi-processes
Can achieve higher concurrency than Apparent
Using asynchronous I/O on a single thread if
I/O processing time is small
Simultaneous use of multiple communication
channels
Using multiple threads where each thread
handles a different communication channel
Transmission Control Protocol (TCP)
semantics
Point-to-point communication
Reliable connection establishment
Reliable delivery
Flow-controlled transfer
Full-duplex transfer
Stream paradigm
Advantage
During an established connection, the reliability of
transmission is provided
No worries in retransmitting lost data and verifying data
integrity and reordering packets

Disadvantage
The above advantage is a kind of overhead if not
required
The server allocates a resource to keep the connection
until a client closes it
It needs to maintain sockets, buffer space and TCP
connections
The service could be denied when it becomes out of
resources
Denial of Service (DoS) attack
User Datagram Protocol (UDP) semantics
Many-to-many communication
Multi-cast or broadcast
Unreliable service
Lack of flow control
Message paradigm
Advantage
No resource depletion issue
Disadvantage
Providing reliability is extremely complicated
Rule of thumbs
List up all the requirements for your service
Understand the TCP & UDP semantics
Check if the requirements fall into one of them
TCP is recommended for novice programmers

The transport protocol when designing a
server should be selected based of the
application protocol semantics to satisfy the
needs
Stateful server
Server behaves differently depending on request
Utilize information maintained in server
E.g. data caching
Stateless server
Each request is handled independently
Server does NOT maintain any client information
If connectionless protocol is used
How can we optimize the stateless server?
Case study
Consider a connectionless file server in which client read
information from files located on the server
Managing small amounts of state can improve
performance
Maintain a table to keep track of which clients downloaded
which file so far
Users dont have to download it from beginning when failed
Must be careful
Resource depletion issue could happen in this case if clients
keep failing such that server have to maintain old states
1. Create a socket
sock = socket(PF_INET, SOCK_STREAM, 0);
2. Bind to a local address
bind(sock, localaddr, addrlen)
For well-known server, use getservbyname(name,
protocol) to get port number from service name
Often use wildcard address (INADDR_ANY) for host IP
3. Place socket in passive mode
listen(sock, queuelen)
Need to establish queue length
4. Accepting a connection from a client
new_sock = accept(sock, addr, addrlen);
accept() blocks until there is an incoming request
Based on the queue length specified in listen()
call, incoming connection requests may be accepted
by the operating system and queued to be accepted
later by the application server using accept()
New socket descriptor (new_sock) is used to send
and receive messages with the connected client.
5. Interact with client using new_sock
Depending on application-level protocol
Send and receive message using the connected
socket
send(new_sock, ) or write(new_sock, )
recv(new_sock, ) or read(new_sock, )

6. Close the connection and return to step 4 to
accept a new connection
close(new_sock)
Chapter 10.5
DAYTIME server
Server blocks until a client connects
Client sends no data
time() returns a 32-bit integer that gives the
current time in seconds since the epoch (1/1/1970)
ctime() converts the epoch seconds into human-
readable character string
Close the connection
Is it safe here?
A deadlock occurs when
The client waits for the server
The server waits for the client

A simple deadlock
Server Client

accept () <-----> connect ()
| |
recv () recv ()
Deadlocks can happen in a more subtle way
Why deadlock in the following scenario?









The OS has a limited buffer and the system call in
server is blocked until the buffer becomes available
--->
|
----
Server Client

accept () <-----> connect ()
| |
recv () send (BIGBUFFER)
|
send ()
1. Create a socket
sock = socket(PF_INET, SOCK_DGRAM, 0);

2. Bind to a local address
Similar to connection-oriented server

3. Interact with one or more clients
recvfrom(sock, buf, buflen, flags, from_addr,
from_addrlen)
Each subsequent recvfrom() can be from a different client
Server identifies client by from_addr
sendto (sock, buf, buflen, flags, to_addr, to_addrlen)
to_addr is usually from_addr from previous recvfrom() call

Chapter 9.4
TIME server listens at port 37
recvfrom() to receive the datagram sent by
the client
Read only one byte
What about the remaining data?
time() returns the number of seconds in
EPOCH
Converting the number of seconds to network
byte order before sending to the client!
(1) Iterative
Connection-oriented
(2) Iterative
Connectionless
(3) Concurrent
Connection-oriented
(4) Concurrent
Connectionless
Common design architecture
Master-slave concept
Master accepts requests and forward them to slaves for
service
Slave handles each request from client
Single-threaded, multi-processes master-slave
model
Multi-threaded, single-process master-slave
model
Multi-threaded, multi-processes master-slave
model
E.g. Apache Web server
M1. Create a socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
M2. Bind to a local address
bind(sock, localaddr, addrlen)
For well-known service, use getservbyname(name,
protocol) to get port number from service name
Often use wildcard address (INADDR_ANY) for host IP
M3. Receive the next request from a client
recvfrom(sock, buf, buflen, flags, from_addr,
from_addrlen)
Create a new process (fork) or thread
(pthread_create) to handle the request
S1. Process the request from the master
sendto(sock, buf, bulen, flags, toaddr,
toaddrlen)
Send the reply to the client using sendto()
Socket number (sock) is inherited from the master
S2. exit
M1. Create a socket
sock = socket(PF_INET, SOCK_STREAM, 0)
M2. Bind to a local address
bind(sock, )
M3. Place the socket in passive mode
listen(sock, )
M4. Repeatedly call accept() to receive the
next request from a client
accept(sock, )
M5. Create a new slave to handle the request
S1. Process the request from the master
Use write(new_sock, ) and read(new_sock, )
to interact with clients
S2. Close the connection and exit
Example code
Chapter 11.5
How parent and child processes close master and
slave sockets?
The life cycle of a child process
Creation
Exit (zombie state)
Maintain information of child process (PID, resource
utilization, exit status)
Waited by its parent
Zombie processes take up system resources
How to clean zombie processes?
Kernel sends SIGCHLD signal to parent process
POSIX compatible functions calls
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc,
int options)
BSD compatible calls
#include <sys/wait.h>
int wait3(int *status, int options,
struct rusage *rusage);
WNOHANG option enables function to return
if there is no terminated child
Signals can be sent
By one process to another (or to itself)
By the kernel to a process
Every signal has a default action associated
with it
Terminating a process
E.g. SIGPIPE, SIGKILL
Ignoring the signal
E.g. SIGCHLD, SIGURG (out-of-band data)
We can change the default action by installing
a signal handler function
signal(int signo, void (*func)(int))
Installing signal handler
signal(SIGCHLD, reaper)
Signal handler function
void reaper(int sig)
{
int status;
while (wait3(&status, WNOHANG,
(struct rusage *)0) >= 0);
}
When a process is blocked in a slow system call
(e.g. accept) and the process catches a signal and
the signal handler returns, the system call can
return an error of EINTR
Some kernels restart interrupted system calls, others
dont.

ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
if (ssock < 0) {
if (errno == EINTR)
continue;
errexit(accept: %s\n, strerror(errno));
}
We must catch SIGCHLD signal when forking
child processes
A SIGCHLD handler must be coded correctly
using wait3() or waitpid() to prevent zombies
from being left behind
We must handle interrupted system calls
when we install signal handlers
Connection abort before accept returns
RST received by TCP
Some implementations dont return error, but
others may return ECONNABORTED (POSIX) or
EPROTO (SVR4)
Termination of server process
Start the server and client and type one line on the
client. Verify that line is echoed back
Kill the slave process on the server
Type a line of input to the client, what happens?
Crashing of server host
Nothing is sent from the server to the client
The client keeps retransmitting its TCP segments until
timeout
BSD attempts 12 retransmissions (9 minutes)
ETIMEDOUT returned to client process
Crashing and rebooting of server host
After rebooting, the server loses old connection
information, so it responds with RST
The client TCP returns ECONNRESET to the client process
What happens if the server host is shutdown
normally?

You might also like