【UNP_socket编程】多进程服务器模型

文章目录

        • 1.多进程服务器模型简介
          • 1.1服务器介绍
          • 1.2客户端介绍
        • 2.边界条件处理
          • 2.1处理僵死进程和处理被中断的系统调用
          • 2.2accept返回前连接中止
          • 2.3服务器进程中止
          • 2.4服务器进程中止(二)
          • 2.5服务器主机崩溃
          • 2.6服务器主机崩溃后重启
          • 2.7服务器主机关机

1.多进程服务器模型简介

【UNP_socket编程】多进程服务器模型_第1张图片

1.1服务器介绍

服务器模型采用了阻塞式,父进程阻塞与accept,当有新进程到达时,通过fork产生一个子进程专门用于处理与客户端的连接。

服务器端处理业务部分process(connectefFd,serverAddress)同样采用阻塞式,进程阻塞于read(),直到读到数据,经过处理,生成响应报文,然后write()发送报文。

1.2客户端介绍

不同于服务器,客户端不仅需要维护与服务器建立的套接字,还需要维护用户输入相关套接字。所以采用阻塞式会影响效率,故可以用select或poll。

2.边界条件处理

2.1处理僵死进程和处理被中断的系统调用

考虑如下情景:

服务器端有一个进程在特定端口监听,阻塞于accept调用。服务器端还有若干个子进程分别与若干个客户端建立了连接。

此时,有一个或多个客户端完成了与服务器交互,关闭了socket连接。在服务器子进程与客户端进程完成了四次挥手之后,客户端进程退出。服务器端子进程也退出,但是退出的子进程会向父进程(监听进程,即阻塞与accept调用的进程)发送一个SIGCHLD信号来告知父进程子进程的退出,如果父进程不对其进行处理,则子进程会进入僵死状态,一直占用服务器资源,造成资源浪费。故父进程需要对子进程发出的信号进行处理。同时,由于父进程阻塞于accept调用(accept调用是慢系统调用,同时慢系统调用也可以用来描述可能永远阻塞的调用),当捕获到某个信号且相应信号处理函数返回时,该慢系统调用(此处即accept调用)会返回一个EINTR错误,需要对此错误进行处理(当然有些内核会重启某些被中断的系统调用,但为了代码可移植性,需要进行完善处理,不能无视此错误)。

总的来说,服务器进程需要对两个事情进行处理:1.由于子进程结束而向父进程发送了一个SIGCHLD信号,父进程需要对此信号进行处理。2.父进程由于捕获SIGCHLD信号而可能使得accept调用返回EINTR错误,需要对此错误进行处理。

解决办法:
对于第一件事情:

    listen(listenFd,LISTEN_QUEUE_NUM);
    signal(SIGCHLD,sig_child);//listen调用后利用signal函数进行对SIGCHLD信号的处理,处理方式在sig_child函数中
	void sig_child(int signo){
	    pid_t pid;
	    int stat;
	
	    while((pid = waitpid(-1,&stat,WNOHANG)) > 0){//由于信号不会进行排队,为了不遗漏采用while循环和waitpid
	        //printf("child %d terminated\n",pid);
	    }
	    
	    return ;
	}

对于第二件事情:

    while(1){
        connectFd = accept()

        if(connectFd<0){//accept error
            if(errno == EINTR)//处理由于捕获信号而返回了EINTR错误
                continue;
            else{//出现错误 退出
                exit(1);
            }
        }else{//accept success
           fork()
           ......
        }
    }


2.2accept返回前连接中止

考虑如下情景:

当客户端与服务器完成三次握手,建立连接以后。服务器accept还没来得及将连接从listen维护的已建立连接队列中取出来,此时客户端发送了一个RST(复位)。

2.3服务器进程中止

考虑如下场景:

在服务器和客户端建立连接后,利用kill命令杀死服务器子进程(其返回给父进程的信号也被正确处理)。作为进程终止处理的部分工作,服务器会单方面关闭连接(即服务器发送FIN,客户响应ACK)。但此时客户端认为是服务器不会在发送数据,但是会接受服务器发送的数据。此时,客户端又有数据要向服务器发送。当服务器TCP收到来自客户端的数据时,既然先前打开套接字的进程已经终止,于是响应一个RST。客户端在发送完数据后会等待读取服务器响应报文,就会读到FIN,read调用返回0。然后客户端就会因为未收到预期数据而终止。

2.4服务器进程中止(二)

接2.3场景进一步考虑:

要是客户端不理会read调用返回的0,继续通过write向服务器发送报文。此时就会出现错误。

因为当一个进程向某个已经收到RST的套接字继续执行写操作时,内核会发送一个SIGPIPE信号,该信号的默认行为是中止进程,故客户端进程会被中止。同时写操作会返回EPIPE错误。

2.5服务器主机崩溃

考虑如下情景:

当客户端与服务器建立连接后,由于网络问题或服务器主机问题,服务器在已有的网络连接上发送不出任何东西。(考虑和关机的区别??)此时,当客户端write数据后,由于等不到服务器的响应,客户端会认为丢包,TCP会持续重传该报文,直到超时放弃重传。当放弃重传后,会给正在等待在read调用的客户端返回一个ETIMEOUT错误,(如果通过中间某个路由器判定服务器主机不可达,会返回EHOSTUNREACH或ENETUNREACH错误)。显然等待超时会耗费很多时间,我们如果需要更快的检测出这种情况,就需要对read调用设置一个超时,后面讨论。???

2.6服务器主机崩溃后重启

在2.5的情况下考虑如下情景:

客户因为收不到服务器的响应报文而持续重传,但是在超时前服务器又恢复了正常,但此时的恢复正常是服务器能够进行网络通信,但是服务器因为崩溃而导致丢失了之前的所有TCP连接信息,因此服务器对于所收到的来自客户的数据分节响应一个RST。

当客户端收到RST时,客户端正阻塞与read调用,导致该调用返回ECONNRESET错误。???如何处理,不处理会怎么样

2.7服务器主机关机

考虑如下情景:

当服务器主机关机时,会向所有正在运行的进程发送SIGTERM信号,等待一段时间后发送SIGKILL信号。如果服务器不捕获SIGTERM信号,则服务器将会被SIGKILL信号终止,所有打开的描述符都会被关闭。之后的情况就和服务器进程终止一样。故需要在客户中使用select或poll函数,使得服务器进程终止一发生,客户就能检测到。

你可能感兴趣的:(网络编程)