tcp client server example n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
TCP Client-Server Example PowerPoint Presentation
Download Presentation
TCP Client-Server Example

Loading in 2 Seconds...

play fullscreen
1 / 80

TCP Client-Server Example - PowerPoint PPT Presentation


  • 113 Views
  • Uploaded on

TCP Client-Server Example. Unix Network Programming Chapter 5. fgets. stdin. TCP client. TCP server. writen. readline. stdout. readline. writen. fputs. Introduction. 1. The Client reads a line of text from its standard input and writes the line to the server

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'TCP Client-Server Example' - marina


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
tcp client server example

TCP Client-Server Example

Unix Network Programming

Chapter 5

introduction

fgets

stdin

TCP

client

TCP

server

writen

readline

stdout

readline

writen

fputs

Introduction

1. The Client reads a line of text from its standard input and writes the line to the server

2. The server reads the line from its network input and echoes the line back to the client

3. The client reads the echoed line and prints it on its standard output

boundary conditions
Boundary Conditions
  • What happens when the client and server are started?
  • When happens when the client terminates normally?
  • What happens to the client if the server process terminates before the client is done?
  • What happens to the client if the server host crashes?
  • and so on
tcp echo server
TCP Echo Server
  • #include <stdio.h>
  • •#include <stdlib.h>
  • •#include <unistd.h>
  • •#include <errno.h>
  • •#include <string.h>
  • •#include <sys/types.h>
  • •#include <sys/socket.h>
  • •#include <netinet/in.h>
  • •#define SERV_PORT 9876
  • •#define BACKLOG 10
  • •#define MAXDATASIZE 100
tcp echo server1
TCP Echo Server

#include <unp.h>

int main(int argc, char **argv)

{

int listenfd, connfd;

pid_t childpid;

socklen_t clilen;

struct sockaddr_in cliaddr, servaddr;

listenfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(SERV_PORT);

Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

Listen(listenfd, LISTENQ);

tcp echo server2
TCP Echo Server

for ( ; ; )

{

clilen = sizeof(cliaddr);

connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

if (Childpid = Fork())== 0) /* child process */

{

Close(listenfd); /* close listening socket */

str_echo(connfd); /* process the request */

exit(0);

}

Close(connfd); /*parent closes connected socket*/

}

}

tcp echo server str echo
TCP Echo Server: str_echo()

void str_echo(int sockfd)

{

ssize_t n;

char line[MAXLINE];

for ( ; ; )

{

if ( (n = Readline(sockfd, line, MAXLINE)) == 0)

{

return; /* connection closed by other end */

}

Writen(sockfd, line, n);

}

}

tcp echo client
TCP Echo Client
  • #include <stdio.h>
  • •#include <stdlib.h>
  • •#include <unistd.h>
  • •#include <errno.h>
  • •#include <string.h>
  • •#include <sys/types.h>
  • •#include <sys/socket.h>
  • •#include <netinet/in.h>
  • •#define SERV_PORT 9876
  • •#define BACKLOG 10
  • •#define MAXDATASIZE 100
tcp echo client1
TCP Echo Client

int main(int argc, char **argv)

{

int sockfd;

struct sockaddr_in servaddr;

sockfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(SERV_PORT);

Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

str_cli(stdin, sockfd); /* do it all */

}

tcp echo client str cli
TCP Echo Client: str_cli()

void str_cli(FILE *fp, int sockfd)

{

char sendline[MAXLINE], recvline[MAXLINE];

while (Fgets(sendline, MAXLINE, fp) != NULL)

{

Writen(sockfd, sendline, strlen(sendline));

if (Readline(sockfd, recvline, MAXLINE) == 0)

{

err_quit("str_cli: server terminated prematurely");

}

Fputs(recvline, stdout);

}

}

normal startup
Normal Startup

Only Server Running

  • tcpserv &
  • netstat –a

Proto Local Address Foreign Address State

tcp *.9877 *.* LISTEN

Client and Server Running

  • tcpcli 127.0.0.1
normal startup1
Normal Startup
  • Client calls socket and connect, the latter causing TCP's three-way handshake to take place
  • When the three-way handshake completes, connect returns in the client and accept returns in the server
normal startup2
Normal Startup
  • The connection is established and following steps then take place:
    • Client calls str_cli, which will block in the call to fgets, because we have not typed a line of input yet
    • When accept returns in the server, it calls fork and the child server calls str_echo
      • This function calls Readline, which calls read, which blocks while waiting for a line to be sent from the client
    • The server parent, on the other hand, calls accept again, and blocks while waiting for the next client connection
  • There are three processes, and all three are asleep (blocked): client, server parent, and server child
normal startup3
Normal Startup

Client and Server Running

  • netstat –a

Proto Local Address Foreign Address State

tcp localhost.9877 localhost.1052 Established

tcp localhost.1052 localhost.9877 Established

tcp *.9877 *.* LISTEN

normal startup4
Normal Startup
  • ps -l

PID PPID WCHAN Command

19130 19129 wait -ksh

21130 19130 netcon tcpserv01

21131 19130 ttyin tcpcli01 (127.0.0.1)

21132 21130 netcon tcpserv01

normal termination
Normal Termination
  • tcpcli 127.0.0.1

hello, world

hello, world

good bye

good bye

^D (control-D is our terminal EOF character)

  • netstat -a | grep 9877

tcp 0 0 *:9877 *:* LISTEN

tcp 0 0 localhost:42758 localhost:9877 TIME_WAIT

sequence of termination
Sequence of Termination
  • When we type EOF character, fgets returns a null pointer and the function str_cli returns
  • When str_cli returns to the client main function the latter terminates by calling exit()
  • Kernel at the client closes the socket (all open descriptors)
    • A FIN is sent to the server, to which the server TCP responds with an ACK
    • Server socket is in the CLOSE_WAIT state and client socket is in the FIN_WAIT_1 state
sequence of termination1
Sequence of Termination
  • When the server TCP receives the FIN, the server child is blocked in a call to Readlineand then returns 0
    • This causes the str_echo function to return to the server child main
  • The server child terminates by calling exit()
  • All open descriptors in the server child are closed
    • Causes the final two segments of the TCP connection termination to take place: a FIN from server to the client, an ACK from the client
    • At this point the connection is completely terminated
    • The client socket enters the TIME_WAIT state
process termination
Process Termination
  • Another part of the process termination is for the SIGCHLD signal to be sent to the parent when the server child terminates
  • This also occurs in this example but the code (at present) does not catch this signal, and the default action of this signal is to be ignored
  • The child enters the zombie state
  • ps

PID STAT COMMAND

19130 Ss -ksh

21130 I tcpserv

21132 Z tcpserv (Z:zombie process)

  • Cleaning up of zombie processes requires dealing with Unix Signals
signal
Signal
  • Signals, are notifications sent to a process in order to notify it of various "important" events
  • By their nature, they interrupt whatever the process is doing and force it to handle them immediately
  • Each signal has an integer number that represents it (1, 2 and so on), as well as a symbolic name that is usually defined in the file /usr/include/signal.h
  • Use the command 'kill -l' to see a list of signals supported by your system
  • Each signal may have a signal handler, which is a function that gets called when the process receives that signal
signal1
Signal
  • Signals usually occurs asynchronously
    • Process does not know ahead of time exactly when a signal will occur
  • When the signal is sent to the process, the OS stops the execution of the process, and "forces" it to call the signal handler function
  • When that signal handler function returns, the process continues execution from wherever it happened to be before the signal was received
  • Similar to interrupts
    • Interrupts are sent to the OS by the hardware
    • Where as signals are sent to the process by the OS, or by other processes
sending signals to processes
Sending Signals to Processes
  • Sending signals using keyboard
  • Ctrl-C
    • OS send an INT signal (SIGINT) to the running process
    • By default, this signal causes the process to immediately terminate
  • Ctrl-Z
    • OS send a TSTP signal (SIGTSTP) to the running process
    • By default, this signal causes the process to suspend execution
  • Ctrl-\
    • OS send a ABRT signal (SIGABRT) to the running process
    • By default, this signal causes the process to immediately terminate
sending signals from command line
Sending Signals from Command Line
  • kill
    • Accepts two parameters: a signal name (or number), and a process ID
    • kill - <signal> <PID>
    • kill -INT 5342
    • Same affect as pressing Ctrl-C
  • fg
    • Resumes execution of the process (that was suspended with Ctrl-Z), by sending it a CONT signal
sending signals using system calls
Sending Signals Using System Calls
  • kill system call
    • Normal way of sending a signal from one process to another
    • Also used by the 'kill' command or by the 'fg' command
    • In the following code, a process suspend its own execution by sending itself the STOP signal

#include <unistd.h>  

#include <sys/types.h> 

#include <signal.h>

/* first, find my own process ID */

pid_tmy_pid = getpid();

/* now that i got my PID, send myself the STOP signal. */

kill(my_pid, SIGSTOP);

  • An example of a situation when this code might prove useful, is inside a signal handler that catches the TSTP signal (Ctrl-Z) in order to do various tasks before actually suspending the process
catching signals signal handlers
Catching Signals - Signal Handlers
  • Catchable And Non-Catchable Signals
    • Most signals may be caught by the process, but there are a few signals that the process cannot catch, and cause the process to terminate
      • kill -9
    • One process that uses this signal is a system shutdown process
      • First sends a TERM signal to all processes, waits a while, and after allowing them a "grace period" to shutdown cleanly, it kills whichever are left using the kill signal
catching signals signal handlers1
Catching Signals - Signal Handlers
  • STOP is also a signal that a process cannot catch, and forces the process's suspension immediately
  • Useful when debugging programs whose behavior depends on timing
  • Example
    • Suppose that process A needs to send some data to process B, and you want to check some system parameters after the message is sent, but before it is received and processed by process B
    • One way to do that would be to send a STOP signal to process B, thus causing its suspension, and then running process A and waiting until it sends its important message to process B
    • Now you can check whatever you want to, and later on you can use the CONT signal to continue process B's execution, which will then receive and process the message sent from process A
catching signals signal handlers2
Catching Signals - Signal Handlers
  • SEGV and BUS signals are catchable
  • Program exiting with a message such as 'Segmentation Violation - Core Dumped', or 'Bus Error - core dumped‘
  • SEGV signal is sent to program due to accessing an illegal memory address
  • BUS signal is sent to program, due to accessing a memory address with invalid alignment
  • In both cases, it is possible to catch these signals in order to do some cleanup - kill child processes, perhaps remove temporary files, etc
catching signals signal handlers3
Catching Signals - Signal Handlers
  • Default Signal Handlers
    • If there is no signal handlers installed, the runtime environment sets up a set of default signal handlers for the program
      • Default signal handler for the TERM signal calls the exit() system call
      • Default handler for the ABRT signal calls the abort() system call, which causes the process's memory image to be dumped into a file named 'core' in the process's current directory, and then exit
catching signals signal handlers4
Catching Signals - Signal Handlers
  • On some systems (such as Linux), when a signal handler is called, the system automatically resets the signal handler for that signal to the default handler
  • Otherwise, the next time this signal is received, the process will exit (default behavior for INT signals)
avoiding signal races masking signals
Avoiding Signal Races - Masking Signals
  • One of the problems that might occur when handling a signal, is the occurrence of a second signal while the signal handler function executes
  • Such a signal might be of a different type then the one being handled, or even of the same type
  • In order to avoid races between the two signals the system also contains some features that will allow us to block signals from being processed
  • These can be used in two 'contexts‘
    • A global context - which affects all signal handlers
    • Per-signal type context - that only affects the signal handler for a specific signal type
masking signals with sigprocmask
Masking Signals with sigprocmask()
  • Posix function used to mask signals in the global context, is the sigprocmask() system call
  • Allows to specify a set of signals to block, and returns the list of signals that were previously blocked
  • Useful to restore the previous masking state once done with critical section
masking signals with sigprocmask1
Masking Signals with sigprocmask()
  • sigprocmask() accepts three parameters
    • int how
      • Add signals to the current mask (SIG_BLOCK)
      • Remove them from the current mask (SIG_UNBLOCK)
      • Replace the current mask with the new mask (SIG_SETMASK)
    • const sigset_t *set
      • Set of signals to be blocked, or to be added to the current mask, or removed from the current mask (depending on the 'how' parameter)
    • sigset_t *oldset
      • If this parameter is not NULL, then it'll contain the previous mask
      • Later use this set to restore the situation back to how it was before calling sigprocmask()
posix signal handling
Posix Signal Handling
  • A signal: a notification to a process that an event has occurred
    • Sometimes called software interrupts
  • Signals can be sent
    • by one process to another process(or itself)
    • by the kernel to a process
  • SIGCHLD signal: a signal sent by the kernel whenever a process terminates, to the parent of the terminating process
posix signal handling1
Posix Signal Handling
  • Every signal has a disposition
    • Action associated with the signal
    • Set the disposition of a signal by calling the sigaction function
    • Two signals are unable to be redefined by a signal handler, and cannot be "caught" by a signal handler
    • SIGKILL always stops a process
    • SIGSTOP always moves a process from the foreground to the background
posix signal handling2
Posix Signal Handling
  • Three choices for the disposition
    • Provide a function that is called whenever a specific signal occurs, this function is called a signal handler and this action is called catching the signal
      • Two signals SIGKILL and SIGSTOP can not be caught
      • void handler(int signo);
  • For most signals, calling sigaction and specifying a function to be called when the signal occurs is all that is required to catch a signal
    • A few signals, (SIGIO, SIGPOLL, SIGURG etc), all require additional actions on the part of the process to catch the signal
posix signal handling3
Posix Signal Handling
  • Ignore a signal by setting its disposition to SIG_IGN
    • Two signals SIGKILL and SIGSTOP can not be ignored
  • Set the default disposition for a signal by setting its disposition to SIG_DFL, the default is normally to terminate a process on the receipt of a signal
signal function
signal() Function
  • Posix establish the disposition of a signal by calling sigaction, however, this gets complicated, as one argument to the function is a structure that must be allocated and filled in
    • int sigaction (int signo, struct sigaction * action, struct sigaction * oldaction);
  • Returns 0 on success and -1 on error
  • struct sigaction {

void (*sa_handler)(int); /*Signal handler*/

sigset_tsa_mask; /*Signals to be blocked during handler execution*/

int sa_flags; /*flags to modify the default behavior*/

}

signal function1
signal() Function
  • Set and unset all of the flags in the given set
    • int sigemptyset (sigset_t *set)
    • int sigfillset (sigset_t *set)
  • Set and unset individual flags, specified by the signal number, in the given set
    • int sigaddset (sigset_t *set, int whichSignal)
    • int sigdelset (sigset_t *set, int whichSignal)
  • All return 0 for success and -1 for failure
signal function2
signal() Function
  • An easier way is to call signal function, with first argument is the signal number and second argument is either a pointer to a function or one of the constants, SIG_IGN or SIG_DFL
    • #include <signal.h>
    • typedef void (*sighandler_t)(int);
    • signal (int signum, sighandler_t handler);
  • The signal() function returns the previous value of the signal handler, or SIG_ERR on error
  • Different implementations have different signal semantics, therefore, book has defined its own signal() function that just calls the Posix sigaction() function
signal function3
signal() Function

#include “unp.h”

Sigfunc * signal(int signo, Sigfunc *func)

{

struct sigaction act, oact;

act.sa_handler = func;

sigemptyset(&act.sa_mask);

act.sa_flags = 0;

if (sigaction(signo, &act, &oact) < 0)

{

return(SIG_ERR);

}

return(oact.sa_handler);

}

posix signal semantics
Posix Signal Semantics
  • Once a signal handler is installed, it remains installed
  • While a signal handler is executing, the signal being delivered is blocked
  • Any additional signals specified in sa_mask signal are also blocked
  • If a signal is generated one or more times while it is blocked, it is normally delivered only one time after the signal is unblocked.
    • By default Unix signals are not queued
  • It is possible to selectively block and unblock a set of signals using the sigprocmask function
handling sigchld signal
Handling SIGCHLD Signal
  • When a server child terminates a SIGCHILD signal is sent to the parent server process and child enters the zombie state
  • The purpose of the zombie state is to maintain information about the child for the parent to fetch at some later time
  • This information includes the
    • Process ID of the child
    • Its termination status
    • Information about the resource utilization of the child (CPU time, memory etc)
  • If a process terminates, and that process has children in the zombie state, the parent process ID of all the zombie children is set to 1 (the init process)
  • init inherits the children and clean them up (i.e., init will wait for them, which removes the zombie)
handling zombies
Handling Zombies
  • Zombies take up space in the memory
  • Whenever fork() must use the functions wait or waitpidto prevent the child process to become zombies
  • Establish a signal handler to catch SIGCHLDand within the handler call wait or waitpidfunctions
    • Establish the signal handler by adding the function call, After the call to listen() in server and before fork
    • Signal (SIGCHLD, sig_chld)
  • Then define the signal handler, the function sig_chld
handling sigchld signals
Handling SIGCHLD Signals

#include “unp.h”

void sig_chld(int signo)

{

pid_tpid;

int stat;

pid = wait(&stat);

printf("child %d terminated\n", pid);

return;

}

handling sigchld signals1
Handling SIGCHLD Signals

tcpserv02 & start server in background

[2] 16939

tcpcli01 127.0.0.1 then start client in foreground

hi there we type this

hi there and this is echoed

^D we type our EOF character

child 16942 terminated output by printf in signal handler

accept error: Interrupted system call main function aborts

handling sigchld signals2
Handling SIGCHLD Signals
  • Terminate the client by typing EOF
  • Client TCP sends a FIN to the server and the server responds with an ACK
  • Receipt of the FIN delivers an EOF to the child's pending readline and child terminates
  • Parent is blocked in its call to accept when the SIGCHLD signal is delivered
  • sig_chld function executes (signal handler)
    • wait fetches the child's PID and termination status
    • printf is called from the signal handler
    • signal handler returns
handling interrupted system call
Handling Interrupted System Call
  • Since the signal was caught by the parent while the parent was blocked in a slow system call (accept), the kernel causes the accept to return an error of EINTR (interrupted system call)
  • Slow system calls – system calls that can block forever
    • accept() may never return if no client connects
    • read() may never return if client sends nothing
    • However, disk I/O may returns to caller assuming some hardware failure
  • The main rule that applies here is that
    • When a process is blocked in a slow system call and
    • The process catches a signal and
    • The signal handler returns, the system call can return an error EINTR
handling interrupted system call restarting the interrupted system call
Handling Interrupted System Callrestarting the interrupted system call

for ( ; ; )

{

clilen = sizeof(cliaddr);

connfd = accept(listenfd,(SA *)&cliaddr,&clilen);

if( connfd < 0 )

{

if( errno == EINTER )

continue;

else

err_sys (“accept error”);

}

}

  • connect() can not be restarted, will return an immediate error
  • When connect() is interrupted by a caught signal that is not automatically restarted, call select to wait for the connection to complete
wait waitpid functions

#include <sys/wait.h>

pid_t wait(int *statloc);

pid_t waitpid(pid_t pid, int *statloc, int option);

both return: process ID if OK,0,or -1 on error

wait & waitpid Functions
  • pid_t: the process ID of the terminated child
  • statloc : the termination status of the child (an integer) is returned through the statloc pointer
  • pid: specify the process ID that we want to wait for
    • A value of -1 tells to wait for the first child to terminate
  • Option: specify additional option
    • WNOHANG – tells the kernel not to block if there are not terminated children; it blocks only if there are children still executing
    • WUNTRACED – also return for children which are stopped but whose status has not been reported
slide50
wait
  • Suspends system execution of the current process
    • Until a child has exited Or
    • Until a signal is delivered whose action is to terminate the current process or to call a signal handling function
  • If the child has already exited by the time of the call (a so-called zombie process), the function returns immediately
  • Any system resources used by the child are freed
waitpid
waitpid
  • Suspends execution of the current process
    • Until a child as specified by the pid argument has exited Or
    • Until a signal is delivered whose action is to terminate the current process or to call a signal handling function
  • If a child as requested by pid has already exited by the time of the call (a so called zombie process), the function returns immediately
  • Any system resources used by the child are freed
  • The value of pid can be one of the value (<-1, -1, 0, >0)
    • - 1 – wait for the child to terminate
difference between wait and waitpid
Difference Between wait and waitpid

Server (Parent)

Server (Childs)

Client

4

3

2

1

0

The client establishes five connections with the server and then uses only the first one (sockfd[0]) in the call to str_cli

difference between wait and waitpid1

SIGCHLD

SIGCHLD

SIGCHLD

SIGCHLD

SIGCHLD

Z

Z

Z

Z

Z

FIN

FIN

FIN

FIN

FIN

Difference Between wait and waitpid

Server (Parent)

Server (Children)

Client

Exit

4

3

2

1

0

  • When the client terminates, all open descriptors are closed automatically by the kernel, and all five connections are terminated at about the same time
  • This causes five FINs to be sent, one on each connection, which in turn causes all five server children to terminate at about the same time
  • This causes five SIGCHLD signals to be delivered to the parent at about the same time
difference between wait and waitpid2
Difference between wait and waitpid

#include “unp.h”

int main (int argc, char **argv)

int main(int argc, char **argv)

{

int i, sockfd[5];

structsockaddr_inservaddr;

for (i = 0; i < 5; i++)

{

sockfd[i] = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(SERV_PORT);

Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

Connect(sockfd[i], (SA *) &servaddr, sizeof(servaddr));

}

str_cli(stdin, sockfd[0]); /* do it all */

exit(0)

}

difference between wait and waitpid3
Difference between wait and waitpid
  • tcpserv03 &
  • [1] 20419
  • tcpcli04 127.0.0.1
  • Hello type this
  • Hello and it is echoed
  • ^D then type our EOF character
  • child 20426 terminated output by server
  • Notice - Only one printf is output, where as all five children to have terminated
difference between wait and waitpid4
Difference between wait and waitpid
  • ps
    • Other four children still exist as zombies
  • PID TTY TIME CMD

20419 pts/6 00:00:00 tcpserv03

20421 pts/6 00:00:00 tcpserv03 <defunct>

20422 pts/6 00:00:00 tcpserv03 <defunct>

20423 pts/6 00:00:00 tcpserv03 <defunct>

difference between wait and waitpid5
Difference between wait and waitpid
  • Establishing a signal handler and calling wait from that handler are insufficient for preventing zombies
  • Problem - All five signals are generated before the signal handler is executed, and the signal handler is executed only one time because Unix signals are normally not queued
  • Client and server on different hosts
    • Signal handler is normally executed two times: once as a result of the first signal being generated, and since the other four signals occur while the signal handler is executing, the handler is called only one more time
    • This leaves three zombies
    • But sometimes, probably dependent on the timing of the FINs arriving at the server host, the signal handler is executed three or even four times
difference between wait and waitpid6
Difference between wait and waitpid
  • Correct solution is to call waitpid instead of wait
  • Call waitpid within a loop, fetching the status of any of our children that have terminated
  • Specify the WNOHANG option
    • This tells waitpid not to block if there are running children that have not yet terminated
wait and waitpid functions

void sig_chld(int signo)

  • {
  • pid_tpid;
  • int stat;
  • while((pid = waitpid(-1,&stat,WNOHANG)) > 0)
  • printf("child %d terminated\n", pid);
  • return;
  • }
wait and waitpid Functions
final version of tcp server
Final Version of TCP Server

#include "unp.h“

int main(int argc, char **argv) {

int listenfd, connfd;

pid_t childpid;

socklen_t clilen;

struct sockaddr_in cliaddr, servaddr;

void sig_chld(int);

final version of tcp server1
Final Version of TCP Server

listenfd = Socket (AF_INET, SOCK_STREAM, 0);

bzero (&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(SERV_PORT);

Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

Listen(listenfd, LISTENQ);

final version of tcp server2
Final Version of TCP Server

Signal (SIGCHLD, sig_chld); /* must call waitpid() */

for ( ; ; ) {

clilen = sizeof(cliaddr);

if ( (connfd = accept (listenfd, (SA *) &cliaddr, clilen)) < 0) {

if (errno == EINTR)

continue; /* back to for() */

else

err_sys("accept error");

}

final version of tcp server3
Final Version of TCP Server

if ( (childpid = Fork()) == 0) { /* child process */

Close(listenfd); /* close listening socket */

str_echo(connfd); /* process the request */

exit(0);

}

Close (connfd); /* parent closes connected socket */

}

}

connection abort before accept returns
Connection Abort before accept Returns
  • The three-way handshake completes, the connection is established, and then the client TCP sends an RST(reset)
  • On the server side the connection is queued by its TCP, waiting for the server process to call accept when the RST arrives
  • Some time later the server process calls accept
  • ECONNABORTED is returned
termination of server child process
Termination of Server Child Process
  • Start the server and client and type one line to the client, the line is echoed normally by the server child
  • Find the process ID of the server child and kill it
    • All open descriptors in the child are closed
  • This causes a FIN to be sent to the client, and the client TCP responds with an ACK
    • This is the first half of the TCP connection termination
  • The SIGCHLD signal is sent to the server parent and handled correctly
  • Nothing happens at the client
    • Client TCP receives the FIN from the server TCP and responds with an ACK
    • Problem - client process is blocked in the call to fgets waiting for a line from the terminal
termination of server child process1
Termination of Server Child Process

netstat -a | grep 9877

tcp 0 0 *:9877 *:* LISTEN

tcp 0 0 localhost:9877 localhost:43604 FIN_WAIT2

tcp 1 0 localhost:43604 localhost:9877 CLOSE_WAIT

termination of server child process2
Termination of Server Child Process
  • tcpcli01 127.0.0.1
  • Hello the first line that we type
  • Hello is echoed correctly
  • Here kill the server child on the server host
  • another line then type a second line to the client
  • str_cli : server terminated prematurely
termination of server child process3
Termination of Server Child Process
  • When we type "another line," str_cli calls writen and the client TCP sends the data to the server
  • This is allowed by TCP because the receipt of the FIN by the client TCP only indicates that the server process has closed its end of the connection and will not be sending any more data
  • The receipt of the FIN does not tell the client TCP that the server process has terminated (which in this case, it has)
  • When the server TCP receives the data from the client, it responds with an RST since the process that had that socket open has terminated
termination of server child process4
Termination of Server Child Process
  • The client process will not see the RST because it calls readline immediately after the call to writen and readline returns 0 (EOF) immediately because of the FIN that was received already
  • Our client is not expecting to receive an EOF at this point so it quits with the error message "server terminated prematurely"
termination of server child process5
Termination of Server Child Process
  • When the client terminates (by calling err_quit), all its open descriptors are closed
  • Example depends on the timing
    • The client's call to readline may happen before the server's RST is received by the client, or it may happen after
    • If the readline happens before the RST is received, as shown in example, the result is an unexpected EOF in the client
    • But if the RST arrives first, the result is an ECONNRESET ("Connection reset by peer") error return from readline
termination of server child process6
Termination of Server Child Process
  • Problem
    • Client is blocked in the call to fgets when the FIN arrives on the socket
    • Client is really working with two descriptors—the socket and the user input
    • Instead of blocking on input from only one of the two sources (as str_cli is currently coded), it should block on input from either source
  • One purpose of the select and poll functions
    • As soon as the server child is killed, the client is notified of the received FIN
sigpipe signal
SIGPIPE Signal
  • When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process
  • Default disposition of SIGPIPE: terminate the process
  • If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE
sigpipe signal1
SIGPIPE Signal

void str_cli(FILE *fp, int sockfd)

{

char sendline[MAXLINE], recvline[MAXLINE];

while (Fgets(sendline, MAXLINE, fp) != NULL) {

Writen(sockfd, sendline, 1);

sleep(1);

Writen(sockfd, sendline+1, strlen(sendline)-1);

if (Readline(sockfd, recvline, MAXLINE) == 0)

err_quit("str_cli: server terminated prematurely");

Fputs(recvline, stdout);

}

}

sigpipe signal2
SIGPIPE Signal
  • tcpclill 127.0.0.1
  • hi there we type this line
  • hi there this is echoed by the server
  •   here we kill the server child
  • bye then we type this line
  • Broken pipe this is printed by the shell
crashing of server host
Crashing of Server Host
  • To simulate this run client and server on different hosts
  • Make connection and then disconnect server from the network
  • When the server host crashes, nothing is sent out on the existing network connection
  • Type a line of input to the client, it is written by writen, and is sent by the client TCP as a data segment
  • The client then blocks in the call to readline, waiting for the echoed reply
crashing of server host1
Crashing of Server Host
  • Berkeley-derived implementations retransmit the data segment 12 times, waiting for around 9 minutes before giving up
  • When the client TCP finally gives up an error ETIMEDOUT is returned to the client process
  • But if some intermediate router determined that the server host was unreachable and responded with an ICMP "destination unreachable' message, the error is either EHOSTUNREACH or ENETUNREACH
crashing and rebooting of the server host
Crashing and Rebooting of the Server Host
  • Start the server and then the client and type a line to verify that the connection is established
  • The server host crashes and reboots
  • Type a line of input to the client, which is sent as a TCP data segment to the server host
  • When the server host reboots after crashing, its TCP loses all information about connections that existed before the crash
  • Therefore, the server TCP responds to the received data segment from the client with an RST
  • Client is blocked in the call to readline when the RST is received, causing readline to return the error ECONNRESET
shutdown of server host
Shutdown of Server Host
  • Server host properly terminated
  • init process sends SIGTERM signal to all the processes to give warning that system is going down
  • After 5 to 20 seconds SIGKILL is sent to the processes
  • Causes process to exit, closing all open descriptors
summary of tcp example
Summary of TCP Example

socket() connet()

client

server

client ephemeral port chosen by TCP

Specify server’s well-known port number

TCP

TCP

client IP address chosen by IP (based on routing)

Specify server’s IP address

IP

IP

data link

data link

data link

data link

Summary of TCP client-server from clients perspective

summary of tcp example1
Summary of TCP Example

socket() bind()

listen() accept()

client

server

Specify server’s well-known port number

return clients port number

TCP

TCP

return clients IP address

Specify server’s IP address

IP

IP

data link

data link

data link

data link

Summary of TCP client-server from server’s perspective