unp第五章总结(网络编程中出现的一些问题情况)

子进程结束未被处理成为僵死进程

  1. 子进程完成任务后会向父进程发送SIGCHLD信号,父进程必须捕获信号,在信号处理函数中调用wait或waitpid来处理已终止的子进程。
  2. 当程序阻塞在慢系统调用(如accept)时捕获到信号时,内核会返回一个EINTR错误给被中断的系统调用,编写程序时一定认清被中断的系统调用且处理它们。通常的做法是重新循环重启被中断的系统调用。
  3. connect是一个例外。如果该函数返回一个EINTR,我们就不能再次调用他,否则将立即返回一个错误。当connect被一个捕获的信号中断而且不自动重启时,我们必须调用select来等待连接完成,具体见unpv1 16.3节。
  4. 因为信号是不排队的,所以当多个信号在信号处理函数执行之前产生,只会执行一次信号处理函数,如果在执行一次信号处理函数时有其他多个信号到来,也只会执行一次。正确的解决方法时调用waitpid而不是wait,在循环中获取所有已终止子进程的状态,并指定WNOHANG选项,使没有僵死进程后停止循环,而wait函数无法做到。
void sig_chld(int signo)
{
	pid_t pid;
	int stat;
	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
		printf("child %d terminated\n", pid);
	return;
}

accept返回前连接终止

这个情况依据不同的内核的实现,有的直接kill掉,有的直接成功返回,但是在read时报错

服务器进程终止

以一个普通的echo程序为一个例子,当一个服务器进程崩溃(注意是进程崩溃而非服务器主机崩溃),进程会关闭所有打开的描述符。导致会向客户发送一个FIN,客户TCP响应一个ACK,完成了四次挥手的前半部分。
因为客户端大部分时间都是阻塞在获取用户输入上,所以客户端不会立刻知道连接已经断开。
当客户输入一行数据时发送,因为TCP关闭连接是双全工的,所以可以发送数据给服务端,服务器收到数据时,此前的进程已经终止,于是响应一个rst,但是客户端发送完数据后又立即调用read,由于之前收到了FIN所以返回0,于是退出,所以客户端实际是没有看到rst的。

这个情况的问题在于客户端并没有立刻响应服务器的FIN,原因是客户端阻塞在用户输入上,要解决这个问题,只需要使用IO复用技术,使程序阻塞在任何输入上而不只是其中一个。

SIGPIPE信号

接着上一个例子,如果客户端没有理会read的返回值而继续write的话会产生什么样的结果?
当一个进程向某一个已收到RST的套接字执行写操作时,内核向该进程发送一个SIGPIPE信号。该信号的默认行为是终止进程,因此进程必须捕获它以免不情愿地被终止。
不管对 信号做什么操作,写操作都会返回EPIPE错误。

这种情况是有可能发生的,如果你连续执行两次写操作,第一次将引发RST,第二次写将引发SIGPIPE。

解决方法是捕获或忽略该信号,来对他进行特殊处理。

服务器主机崩溃

当服务器主机崩溃时,客户端将执行重传,重传数次失败后,断开连接。
如果客户端阻塞在read上,返回值会有不同种情况:

  • EHOSTUNREACH 错误(网络存在,但是主机不存在)
  • ENETUNREACH(网络不存在,可能是中间路由器宕机了)
  • ETIMEDOUT 主机存在,但是主机根本不响应。

如果想要更快检测出这种情况,可以对read设置超时,如果不主动发送数据,想要检测出,可以使用keepalive技术。

服务器崩溃后重启

此时服务器已经丢失了所有的连接信息,将响应一个RST

当客户端阻塞与read时,将返回ECONNRESET错误。

服务器主机关机

当关机时,会先向所有进程发送SIGTERM信号,该信号默认终止进程,此时服务器情况与服务器进程终止情况一致,一段时间后,系统会给所有进程发送SIGKILL信号,该信号不能被捕获。

发布了18 篇原创文章 · 获赞 0 · 访问量 288

猜你喜欢

转载自blog.csdn.net/weixin_42162340/article/details/103489882
今日推荐