39-连接断开异常(引发 SIGPIPE)

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。

本次实验是接着上一篇文章的实验继续的,所以大家最好两篇连着看。上一篇文章的地址:《连接断开异常(服务器进程终止)》

1. 相关程序

本次实验所使用的程序路径是 unp/program/echo/exception_sigpipe。这个程序与之前的 processzombie 并没有什么本质上的不同,几乎完全一样,只不过,我们做了两个修改

  • 捕捉了 SIGPIPE 信号
  • 客户端部分稍稍做了修改,如下:
void doClient(int sockfd) {
  int nr, nw;
  char buf[4096];

  while(fgets(buf, 4096, stdin)) {
    // 只是将以前的一行 writen 分成了两次
    nw = writen(sockfd, buf, 1);
    sleep(1);// 等待 1 秒,保证收到 RST 段
    nw = writen(sockfd, buf + 1, strlen(buf + 1));
    if (nw < strlen(buf + 1)) puts("short write");

    nr = readline(sockfd, buf, 4096);
    if (nr == 0) {
      puts("peer closed");
      break;
    }
    else if (nr < 0) ERR_EXIT("readline");

    write(STDOUT_FILENO, buf, nr);
  }
}

这样修改的目的是什么呢?我们的实验步骤其实上上一篇文章完全一样,即让服务器进程终止,客户端再不知道服务器终止的情况下,又向服务器发送数据。

客户端分成 2 次 writen,第一次发送 1 个字节,第二个发送 n - 1 个字节,两次 writen 之间休眠 1 秒。这样做的目的是希望在第二次 writen 前收到对端发来的 RST 段。

2. 实验结果


这里写图片描述
图1 服务器端,杀死子进程


这里写图片描述
图2 客户端,第二次发送 hehe 收到 SIGPIPE 信号

3. 结果分析

3.1 为什么服务器收到两次 SIGCHLD

很奇怪的是,服务器端在杀死子进程时,我们收到了两个 SIGCHLD 信号,不知道大家有没有注意,在上一篇文章里实际上也是这种情况。因为当你按下 CTRL Z 的时候,会导致父进程和子进程都停止。

而 SIGCHLD 信号产生需要满足以下任意一个条件之一:

  • 子进程结束
  • 子进程停止
  • 子进程恢复执行(man 手册中并没有写这种情况,不知道是不是和内核有关,希望大家进行测试并进行留言)

因此,一次收到 SIGPIPE 是因为子进程停止,另一次是因为子进程终止。

3.2 为什么收到 SIGPIPE

接下来,我们分析为什么客户端会接收到 SIGPIPE。当 flower 服务器结束的时候,sun 客户端第一次 writen 后,服务器会回送 RST 端,接着 sun 等待了 1 秒,这一秒时间,足以让 sun 接收到 RST 段,此时 sun 又执行了一次 writen,于是引发了 SIGPIPE,同时 writen 错误,errno = EPIPE.

《Unix 网络编程》 是这样解释的:

当一个进程向某个已收到 RST 段的套接字执行写操作时,内核向该进程发送一个 SIGPIPE 信号。

4. 总结

  • 向收到 RST 套接字执行写,会引发 SIGPIPE
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值