说在前面
- 环境: WSL、ubuntu16
- 参考: UNIX网络编程、linux manual page
- 目录:这里
问题提出
-
在⟅UNIX网络编程⟆⦔处理SIGCHLD信号提及了如何处理被中断的系统调用的一种情况(EINTR非致命错误),另一种情况也可能导致一种非致命错误。
在三路握手完成而建立连接之后,客户TCP却发送了一个RST(复位)。
在服务端看来,该连接已由TCP排队,等待accept返回(见⟅UNIX网络编程⟆⦔listen函数)时,客户的RST到达(这时,该连接理应终止)。而后,将调用accept函数。 -
模拟问题
启动服务器,在其调用accept前让其sleep一小段时间。
在服务器睡眠时,启动客户,在connect返回后设置SO_LINGER套接字选项让其产生RST,然后终止。(在ubuntu下,当前这种模拟并没有产生错误并设置errno,在后面还会涉及该问题,暂时搁置)// client Connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); struct linger lng; socklen_t nSize = sizeof(lng); lng.l_onoff = 1; lng.l_linger = 0; if( setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lng, nSize) != 0) err_sys("setsockopt error"); Close(sockfd);
// server sleep(20); for ( ; ; ) { clilen = sizeof(cliaddr); if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0) { err_sys("accept error"); }
解决方式
- 如何处理这种中止的连接依赖于不同的实现。
- 源自Berkeley的实现完全在内核中处理中止的连接,服务器进程根本看不到。
- 然而大多数SVR4实现返回一个错误给服务器进程,作为accept的返回结果,不过错误本身取决于实现。这些SVR4实现返回一个EPROTO (“protocolerror",协议错误)errno值
- 而POSIX指出返回的errno值必须是ECONNABORTED(“software caused connection abort",软件引起的连接中止)。
POSIX作出修改的理由在于:流子系统(streams subsystem)中发生某些致命的协议相关事件时,也会返回EPROTO。要是对于由客户引起的一个已建立连接的非致命中止也返回同样的错误,那么服务器就不知道是否应该再次调用accept。换成ECONNABORTED错误,服务器就可以忽略它,再次调用accept就行。