Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 TCP Sockets Computer Network Programming. 2 TCP Echo Server We will write a simple echo server and client –client read a line of text from standard.

Similar presentations


Presentation on theme: "1 TCP Sockets Computer Network Programming. 2 TCP Echo Server We will write a simple echo server and client –client read a line of text from standard."— Presentation transcript:

1 1 TCP Sockets Computer Network Programming

2 2 TCP Echo Server We will write a simple echo server and client –client read a line of text from standard input and wii send it to the server –server will read the line from network and will write it back to client –the client reads the echoed line from network and prints it on the screen TCP client TCP server stdin stdout fgets fputs writenreadline writen readline

3 3 TCP echo Server #include"unp.h" int main(int argc, char **argv) { intlistenfd, connfd; pid_tchildpid; socklen_tclilen; 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); /* 9877 */ Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); Continued on the next page

4 4 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 */ } Blue code shows the section of the code that is executed by the child process.

5 5 Wrapper Functions int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) err_sys("socket error"); return(n); } void Bind(int fd, const struct sockaddr *sa, socklen_t salen) { if (bind(fd, sa, salen) < 0) err_sys("bind error"); } You can find the wrapper functions in the source code of the examples, in file lib/wrapsocket.c Similarly we have wrapper functions: Accept, Listen, Close, …. For examples we are using wrapper functions so that we are not bothered to handle the error cases.

6 6 str_echo function 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); }

7 7 tcp echo server –TCP echo server is concurrent server –We are creating a new child process for every client request to server that request. –str_echo() function is used to receive and serve the request (read a line and echo it back). –child process uses exit(0) to terminate at which time all the open descriptors belonging to the child process is closed.

8 8 TCP echo client #include "unp.h" int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; if (argc != 2) err_quit("usage: tcpcli "); 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 */ exit(0); }

9 9 str_cli function 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); }

10 10 Running the application parent server child server child server client fork() Port# 9877 Port# 49617 Port# 49616 Run the server and clients on the same host. use the loopback address as the IP address of the host: 127.0.0.1 - localhost Connection requests come to here Echo request/reply

11 11 Running the application Run server first: (server is waiting on port 9877) tcpserv01 & netstat -a | grep 9877 TCP Local Address Remote Address Swind Send-Q Rwind Recv-Q State ------------ -------------------- ----- ------ ----- ------ ------- *.9877 *.* 0 0 0 0 LISTEN Run two clients on two different windows tcpcli01 127.0.0.1 (establish connection to the same host) Execute the netstat command on a third window netstat -a | grep 9877 *.9877 *.* 0 0 0 0 LISTEN localhost.49616 localhost.9877 32768 0 32768 0 ESTABLISHED localhost.9877 localhost.49616 32768 0 32768 0 ESTABLISHED localhost.49617 localhost.9877 32768 0 32768 0 ESTABLISHED localhost.9877 localhost.49617 32768 0 32768 0 ESTABLISHED

12 12 Running the application aspendos{korpe}:> ps -efl | grep tcp 8 S korpe 26623 26613 0 61 20 f63dd718 406 f5e84b8e 12:05:54 pts/17 0:00 tcpcli01 127.0.0.1 8 S korpe 26611 26372 0 61 20 f6230448 406 f62d6b1e 12:05:37 pts/13 0:00 tcpcli01 127.0.0.1 8 S korpe 26595 26360 0 54 20 f65b1780 405 f63aa0d0 11:56:50 pts/12 0:00 tcpserv01 8 S korpe 26624 26595 0 44 20 f66a2898 406 f62d631e 12:05:54 pts/12 0:00 tcpserv01 8 S korpe 26612 26595 0 45 20 f62b0710 406 f5fed4f6 12:05:37 pts/12 0:00 tcpserv01 We can see the processes using the ps command. 3 tcp server processes exists: one parent, two children 2 tcp client processes exists. All processes are sleeping (S) because they are blocking on a function call: parent server: accept() child servers: read() clients: fgets()

13 13 Normally terminating the Client aspendos{korpe}:> netstat -a | grep 9877 *.9877 *.* 0 0 0 0 LISTEN localhost.49617 localhost.9877 32768 0 32768 0 ESTABLISHED localhost.9877 localhost.49617 32768 0 32768 0 ESTABLISHED localhost.49616 localhost.9877 32768 0 32768 0 TIME_WAIT aspendos{korpe}:> aspendos{korpe}:> ps -efl | grep tcp 8 S korpe 27125 27121 0 43 20 f66a1458 406 f5fed6f6 14:10:02 pts/17 0:00 tcpserv01 8 S korpe 27121 26613 0 54 20 f65b1780 405 f63aa0d0 14:09:41 pts/17 0:00 tcpserv01 8 S korpe 27124 26360 0 41 20 f65b2bc0 406 f64df2c6 14:10:01 pts/12 0:00 tcpcli01 127.0.0.1 8 Z korpe 27123 27121 0 0 0:00 (the output of the ps shows different pids, since I had to restart the clients and server) I typed ^D as input to one of the clients. The client terminates.

14 14 Normal Termination –When we type EOF character (^D), fgets returns a NULL pointer and str_cli returns. –Clients call exit(0) –Client process terminates and open descriptors of the client is closed. Hence a FIN segment is sent to the server and an ACK is received. –When TCP server receives FİN, this causes and EOF notification to be passed to the read() and server returns with 0 from readline(). –The server child exits with exit(0). –All open descriptors of child is closed and a FIN is sent to the client and ack ACK is received. Client socket enters to TIME_WAIT state. –The child server process enters to zombie state (look ps command). A SIGCHLD signal is sent to the parent but not handled.

15 15 SIGNALS A signal is a notification to a process that an event has occurred. They are called sometimes software interrupts. Signalls usually occur asynchronously, meaning that the process does not know ahead of time exactly when the signal will occur. Signals can be sent by a process to an other process (or to itself) by kernel to a process kernel process1process2 signal 3-ways that a process can receive signals

16 16 Signals Every signal has a disposition: the action associated with the signal. Sigaction function is called to set the action for a signal. There choices for a disposition Provide a function that will be called whenever a specific signal occurs. This function is called signal handler and this action is called catching the signal. void handler (int signo) SIGKILL and SIGSTOP can not be caught. Ignore the signal by setting the disposition to SIG_IGN. SIGKILL and SIGSTOP can not be ignored Set a default disposition for a signal by setting its disposition to SIG_DFL.

17 17 Signal function Sigfunc * signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */ #endif } if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); } We write our own signal function to set a disposition for a signal. typedef void Sigfunc(int); Sigfunc *signal(int signo, Sigfunc *func);

18 18 Signal Semantics Once a signal handler installed, it remains installed While a signal handler is executing, the signal being delivered is blocked. Additional signals that are specified with sa_mask is also blocked. If a signal is generated one or more times while its is blocked, it is normally delivered only one time after the signal is unblocked. Signals are not queued.. It is also possible to block and unblock signals using sigprocmask function. This lets us protect certain critical region of the code by preventing certain signals from being caught while that region of code is executing.

19 19 Handling SIGCHLD signals Zombie state: the child stays in this state after terminating to maintain information about the child so that the parent can fetch some later time. (process ID, termination status, resource utilization information etc). We don’t want to leave zombies around. They take up space in the kernel (process table). Waiting for a zombie process removes the zombie Hence, in parent, we have to wait for terminating children so that they don’t become zombies »waiting means: waiting until the process terminates and getting the status information after the process terminates.

20 20 Waiting for terminating Children –We should establish a signal handler for SIGCHLD signal: the signal that is sent to the parent when child terminates. –In the signal handler we should wait for the terminating process - reads it terminating status information. –Simply call wait or waitpid functions inside signal handler.

21 21 SIGCHLD handler Establish signal handler by calling signal (SIGCHLD, sig_chld) void sig_chld(int signo) { pid_t pid; int stat; pid = wait(&stat); printf("child %d terminated\n", pid); return; } aspendos{korpe}:> tcpserv02 & [1] 28751 aspendos{korpe}:> tcpcli01 127.0.0.1 test ^D aspendos{korpe}:> child 28753 terminated Run the tcp server and client again Put the signal handler into the tcp server code.

22 22 Observations –We type EOF character on the client, client TCP sends FIN to the server and receives ACK. –The receipt of the FIN delivers EOF to the readline of the child server. Hence child terminates. –The child sends a SIGCHLD signal to the parent server –The parent server is blocked in the call to accept. The accept() is interrupted and signal handler for SIGCHLD is executed (sig_chld) –The signal handler reads the termination status of the child by calling wait and the child is removed from process table - so it does not become a zombie. –The accept is interrupt, hence it return with errno=EINTR, but some systems automatically restart the accept. For example the Solaris Unix that I tested this program was restaring accept automatically without user program knows about it.

23 23 Handling Interrupted system calls Some systems does not restart the interrupted system calls automatically, so it the job of the user program to recall the system call if the error value from previous call was errno = EINTR. We have seen calling the read and write system calls when the error code as EINTR. »Look to the readn() and writen() functions that we have seen earlier.

24 24 Wait and Waitpid Wait() waits for the first terminating child and reads its termination status after the child terminates. (terminated normally, killed, …etc…) If multiple childs exits and they terminate at the same time, all SIGCHLD signals will be coming to the server at the same time. However, the signal handler will catch only one them and the rest will be lost, since the signals are not queued in UNIX. Hence wait() will be executed only once for the first terminating child, and the termination status of the other children will not be read, hence they will become zombies. pid_t wait(int *statloc); pid_t waitpid (pid_t pid, int *statloc, int options);

25 25 Example server Server child1 Server child2 Server child3 Server child4 client exit(0) FIN1 FIN2 FIN3 FIN4 SIGCHLD

26 26 Use of waitid void sig_chld(int signo) { pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; } -1 argument states that we are waiting for the first terminating child WNOHANG argument states that we are not blocking on waiting if there are child processes that are still running. By use of waitpid we can clean up all the child process that are terminates without leaving any zombies behind.

27 27 Abnormal Conditions Server process that handle the request is killed - crashing of server process Crashing of server host Crashing and rebooting server host Shutdown of the server host

28 28 Termination of Server Process Server parent Server child client 1-We kill the child 2-FIN is sent to the client 3-ACK is sent to the server child 0- client blocks on fgets() for user input 4- user enters input line 5 - input line is sent 6 - server TCP replies back with RST since there is no server process waiting on the socket - died. RST 7 -read() returns error and client terminates with connection reset error or some other error a) First, A connection is established between client and server child b) Then the following steps occurs in the order given

29 29 SIGPIPE signal SIGPIPE signal is sent by kernel to a process who tries to write to a socket who received a RST from the peer TCP. We can either catch signal or ignore it. If we ignore than the write operation will return with errno = EPIPE

30 30 Crashing of Server Host Server parent Server child client 1-we type a line of input and send it to the socket by calling write and the client will block on read() or readline() a) First, A connection is established between client and server child b) Then the following steps occurs in the order given Server host 0-we disconnect the server host from the network 2-TCP sends the input line as a TCP segment 3-TCP will not receive an ACK, hence it will timout and will retransmit the segment ………….. 3-TCP will retransmit the segment couple of times (12) since it will not receive any ACK. Finally it will give up after about 9 minutes. 5- read() will return with error ETIMEOUT (EHOSTUNREACH, ENETUNREACH)

31 31 Crashing and rebooting Server Host Server parent Server child client a) First, A connection is established between client and server child b) Then the following steps occurs in the order given 0-we disconnect the server host from the network 4-TCP sends the input line as a TCP segment 6- read() will return with error ECONNRESET 1-we shutdown the machine and then reboot All the information about the existing TCP connections are lost 2-we reconnect the machine to the network 3-we type a line of input and send it to the socket by calling write and the client will block on read() or readline() 5-server TCP replies back with a RST

32 32 Shutdown of Server Host Server parent Server child client a) First, A connection is established between client and server child b) Then the following steps occurs in the order given init Process 1. Init process sends SIGTERM and SIGKILL signals to all processes 0- System Administrator shutsdown the machine by issuing shutdown command 2. Process termites when it receives the SIGKILL signal 3. The sockets belonging to the terminating process are closed by the kernel 4. FIN segment is sent Rest of the operations are the same with “Termination of Server Process”

33 33 Data Formats –Usually server does some processing on the data that is received from the client. –Hence it is important how data is passed between client and server let client pass two integers to the server and the server will add them up and will send the result back to the client. There are two ways to achieve this: »Converting the data into text strings and passing text strings between client and server »Passing binary data between directly between client and server

34 34 Passing Text Strings void str_echo(int sockfd) { long arg1, arg2; ssize_t n; char line[MAXLINE]; for ( ; ; ) { if ( (n = Readline(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */ if (sscanf(line, "%ld%ld", &arg1, &arg2) == 2) snprintf(line, sizeof(line), "%ld\n", arg1 + arg2); else snprintf(line, sizeof(line), "input error\n"); n = strlen(line); Writen(sockfd, line, n); }

35 35 Passing Binary Data - client struct args { long arg1; long arg2; }; struct result long sum; }; void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE]; struct args args; struct result result; while (Fgets(sendline, MAXLINE, fp) != NULL) { if (sscanf(sendline, "%ld%ld", &args.arg1, &args.arg2) != 2) { printf("invalid input: %s", sendline); continue; } Writen(sockfd, &args, sizeof(args)); if (Readn(sockfd, &result, sizeof(result)) == 0) err_quit("str_cli: server terminated prematurely"); printf("%ld\n", result.sum); }

36 36 Passing Binary Data - server void str_echo(int sockfd) { ssize_t n; struct args args; struct result result; for ( ; ; ) { if ( (n = Readn(sockfd, &args, sizeof(args))) == 0) return; /* connection closed by other end */ result.sum = args.arg1 + args.arg2; Writen(sockfd, &result, sizeof(result)); }

37 37 Problems with passing binary data Different systems store binary numbers is different formats: big endian, little endian. Different systems can store the same C datatype differently: some systems use 32 bits for long but some use 64 bits. Different systems pack structures differently Therefore it is now wise to send binary data across a socket

38 38 Two common solutions Pass all numeric data as text strings Explicitly define the binary formats of the supported datatypes: number of bits, little or big endian, etc and pass all data in this format. –RPC for example uses XDR (External Data Representation)


Download ppt "1 TCP Sockets Computer Network Programming. 2 TCP Echo Server We will write a simple echo server and client –client read a line of text from standard."

Similar presentations


Ads by Google