服务器与客户端交互状态和socket编程的关系

正常启动

正常交互就是由 connect 函数发起三次握手,三次握手结束后服务器上的对应的连接节点状态为完全连接状态,这个时候放到完全连接队列,服务器调用accept函数从队列中取得节点返回。
这里写图片描述

正常终止

当我们调用close函数,每调用一次对应的socket的引用计数减一,当到0之后内核就会对该连接发送FIN段终止连接

SIG_CHLD

进程终止的时候会给父进程发SIG_CHLD,这个信号默认是忽略的。已经终止的子进程,它的状态不是dead状态而是Zombie,僵尸态,它会等待父进程查看它的终止信息,一般通过wait/waitpid 得到status。status的低七位为信号值,次低八位为退出码。
那么我们是肯定要处理僵尸进程的,否则占用大量内核资源。所以我们得处理SIG_CHID,有俩种方法
1. 显示设置为SIG_IGN,根据SIGCHLD语义显示设置忽略将代表不关心子进程,那么内核帮我们处理已终止的子进程就不会有僵尸进程了。
2. 捕捉SIGCHLD非阻塞死循环调用waitpid直到显示EAGAIN

信号中断

如果我们正在read/write ,但是这个时候突然触发了一个信号中断,那么这个不能算是一个致命错误,所以我们可以显示处理这个EINTR,重起api即可。

if(read(xx)==0 && errno == EINTR)
   continue;

SA_RESTART也可以重启一些低俗的系统调用,这个选项是在调用signal函数的时候设置的选项,它的含义就是表当面对特定的信号是自动重启被中断的系统调用。
SA_INTERRUPT这个同上使用场景,这个选项的含义是不自动重启系统调用,比如使用alarm函数的时候

ECONNABORTED

这种情况就是服务器在accept返回前,该完全连接节点收到了客户端发送的RST段,这个时候accept调用返回失败,收到ECONNABORTED错误,这个对服务器来说并非致命错误,所以处理方式与EINTR一致。
这种情况是很可能的,有可能服务器并发太大了,很多连接还来不及处理,这个时候对端client一直得不到服务器发送的数据,这个连接一直闲置,所以这个时候client的内核根据一定的时间后就会触发RST关闭闲置连接。
总结一句话就是 Server在accept返回前收到了RST段导致的错误

服务器进程挂掉了客户端发送数据ECONNRESET

如果某种可能对端服务器正常运行但是服务器程序挂掉了,这个时候对端服务器会发送FIN段,然后进入time_wait阶段。这个时候如果客户端向已经终止的服务器程序发送数据,对端主机会发现没有对应连接的已通信状态的节点,它会判断为异常数据,所以会回复RST段。
这个时候客户端就有俩种可能

  1. 先接受了Server的FIN段正常结束
  2. 先接受Server的RST段触发ECONNRESET错误返回

其实这个情况不知是 Client 到 Server的,也可能Client主机的程序挂掉了,我们Server继续发送数据,这个时候可能Server就会接受RST段,这个不是致命错误,所以不能终止Server进程,应该正常处理即可。
总结一句话,接受端已经终止连接,发送段还发送导致的错误。

SIGPIPE

如果对端连接已经终止了,第一次发送会触发RST段,如果我们再发送就会触发SIGPIPE信号了,这个信号默认是终止进程。
对应SIGPIPE的处理有俩种方式

  1. 忽略它
  2. 捕捉它并做一些相应的处理(比如打印一些log,那个链接触发了SIGPIPE)
    如果我们忽略它,那么被中断的系统调用返回EPIPE。

服务器主机怠机了并且没有重启

这个情况比如断电,这个服务器突然断电了,这个时候无论client发送什么都不会得到ack。这个时候client一直超时重传,一般的驱动大概重传12次,然后主动放弃连接,这个时候有俩种情况发生
1. 没有icmp产生,那么就返回ETIMEOUT 错误
2. 对段网段内的路由器返回了一个EHOSTUNREACH / ENETUNREACH 错误

服务器虽然怠机但及时重启

服务器虽然断电了但是及时重启,这个时候client是不知道服务器已经断电了继续发送数据。这个时候重启后的服务器是没有之前通信socket的信息的,所以收到一个莫名的数据段会返回一个RST段给客户端,客户端收到RST返回 ECONNRST。

扫描二维码关注公众号,回复: 2345326 查看本文章

服务器主机关机

关机和断电不一样的,关机有特殊的程序和步骤。比如linux的关机,init进程会向所有进程发送SIGTERM,等待5-20S后,再发送SIGKILL给依然还在跑的进程。所以这个时候所有进程终止,有socket的进程会发送FIN段进入正常终止流程。也就是说对于这个情况,可以看作是正常终止流程。

猜你喜欢

转载自blog.csdn.net/sdoyuxuan/article/details/81160660