Four times to wave

The following figure shows the process of a typical TCP four wave and the state changes of the active and passive closing sides. In this case, the client disconnects voluntarily. This is just an example. The server can also disconnect voluntarily.

Who shut it down?

The client is shut down

If the client closes the connection voluntarily, it is normally closed. First, the client initiates a request to close the connection. After receiving the request, the server sends an ACK to the client to indicate that it has received the request. Then the connection between the client and the server can be closed. In this case, the server does not immediately close the connection, but sends the unfinished data to the client. In this case, the client cannot send data to the server, but it can accept data. This is the semi-shutdown of TCP. At this point, the client is in FIN_WAIT2 and the server is in CLOSE_WAIT(it will remain in this state if the server does not close the connection, which will waste a lot of socket resources if there are many connections). After sending data, the server sends a request to the client to close the connection. After receiving the request, the client confirms to close the connection. In this case, the TCP full-duplex connection is closed.

The server is shut down

If the server initiates the shutdown, the sequence of the four waves is reversed. If the client sends data to the server again, the client will receive an RST reset response (instead of an ACK response) according to the TCP protocol. If the client sends data to the server again, the system will send a SIGPIPE signal to the client process. Tell the client process that the connection is closed and to stop writing. The default processing for the SIGPIPE signal is to terminate the process that received it, so the client process will be terminated reluctantly at this point. If you do not want the client process to terminate, you can define a custom function to handle the signal by calling signal(SIGPIPE, handler), where handler is a custom function. So if the server shuts down and the client continues to write twice, the client process will be terminated (the server cannot receive) and the client cannot write data to the server.


Test program (under Linux)

The service side

#include <stdio.h>
#include 
       
         /* See NOTES */
       
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

// socket(int domain, int type, int protocol);
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int StartUp(char* ip,char* port)
{
     //listen_sock
    int sock=socket(AF_INET,SOCK_STREAM,0);
         if(sock < 0){
                 perror("socket...");
                 exit(- 1);
         }
         struct sockaddr_in sockaddr;
         sockaddr.sin_family=AF_INET;
         sockaddr.sin_port=htons(atoi(port));   //16 bit port
         //sockaddr.sin_addr.s_addr=inet_addr(ip);
         inet_aton(ip,&sockaddr.sin_addr);
         if(bind(sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr))<0){
                perror("bind.. \n");
                exit(2 -);
         }
         struct sockaddr_in addrtmp;
         socklen_t Len;
         getsockname(sock,(struct sockaddr*)&addrtmp,&Len);
         printf("com:%d\n",ntohs(addrtmp.sin_port));

         if(listen(sock,5) <0){
                perror("listen... \n");
                exit(- 3);
         }

         return sock;
}

//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

int main(int argc,char* argv[])
{
        if(argc! =3) {printf("Usg: %s [ip] [port]\n",argv[0]);
                return 3;
        }
        printf("hello servet.. \n");
        int id=fork();
        if(id==0) {printf("this is child... %d\n",getpid());
                printf("debug 1\n");
                int listen_sock=StartUp(argv[1],argv[2]);
                struct sockaddr_in addr;
                socklen_t Len=0;
                while(1) {int fd=accept(listen_sock,(struct sockaddr*)&addr,&Len);
                        printf("It's %s address send message\n",\
                                        inet_ntoa(addr.sin_addr));
                        if(fd < 0){
                                perror("accept... \n");
                                return 2;
                        }
                        else if(fd==0) {printf("client is quit... \n");
                        }
                        char buf[1024];
                        while(1) {ssize_t _s=read(fd,buf,sizeof(buf));
                                if(_s==0) {printf("client is quit.. \n");
                                        break;
                                }
                                else if(_s<0){
                                        perror("read...");
                                        exit(1);
                                }
                                buf[_s- 1] ='\ 0';
                                printf("client #");
                                fflush(stdout);
                                printf("%s\n",buf); }}}else{
                //father proc
                wait(NULL);
        }

        return 0;
}
Copy the code

The client

#include <stdio.h>
#include 
       
         /* See NOTES */
       
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <signal.h>

//int socket(int domain, int type, int protocol);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
void SigpipeRun(int signo)
{
        printf("this is in client SigpipeRun.. ,signo:%d\n",signo);
        sleep(2);
}

int main(int argc,char* argv[])
{
        signal(SIGPIPE,SigpipeRun);
        if(argc! =3) {printf("Usegs: %s [ip] [port]",argv[1]);
                return 3;
        }
        printf("Hello client\n");
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
                perror("socket..");
                exit(1);
        }
        struct sockaddr_in addr;
        addr.sin_family=AF_INET;
        addr.sin_port=htons(atoi(argv[2]));
        addr.sin_addr.s_addr=inet_addr(argv[1]);
        if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))<0){
                perror("connect...");
                return 2;
        }
        printf("connect success... \n");
        char buf[1024];
        while(1) {ssize_t _s=read(0,buf,1024);
                if(_s<0){
                        perror("read... \n");
                        exit(1);
                }
                buf[_s- 1] ='\ 0';
                printf("client #");
                fflush(stdout);
                printf("%s\n",buf);
                write(sock,buf,_s);
        }
        close(sock);
        return 0;
}
Copy the code