服务器开发中的错误处理与连接关闭-结合muduo库

服务器开发中的错误处理与连接关闭

  • RST包和IO函数
    1. 收到RST包的情况

RST包即复位包,一般在连接发生错误的时候由传输层发送的一个包,R ST用来异常的关闭连接。发送RST包关闭连接时,不必等待缓冲区的数据都发出去(普通的close会先将发送缓冲区中的数据发送完毕后再发送fin包),直接就丢弃缓冲区的包发送RST包,而接收端收到RST后,也不必发送ACK确认。常见的收到RST包的情况有以下几种:

  1. 端口未打开,如果服务器端口未打开而客户端来连接。S->C
  2. 客户端请求连接超时。C->S
  3. 提前关闭,当内核读缓冲区中还有数据就关闭连接时,关闭连接的一方会向另一方发送RST
  4. 在一个已经关闭的socket上收到数据,例如如果服务器端在已经关闭掉socket之后,仍然在发送数据。这时服务器会产生RST

https://russelltao.iteye.com/blog/1405349

    1. 收到RST包后的处理

应用程序使用的API必须提供产生异常关闭而不是正常关闭的手段,即API函数返回相应的错误。那么API函数应该怎样去处理这个错误呢?它们又该返回怎么样的错误呢?

  1. read函数收到RST包后,会返回-1,并设置errno为ECONNRESET。
  2. 如果对端连接已经关闭,那么在调用write函数时会正常返回,因为write只是将数据复制到发送缓冲区中,所以会正常返回,当发送方将数据发送给对方时,对端会响应一个RST,这时如果再写则write会返回-1,并设置errno为EPIPE错误,同时内核会向该进程发送一个SIGPIPE信号。
  3. 即如果进程去读写一个已经收到RST的套接字都会发送错误。
  • epoll的事件触发类型

2.1 shutdown函数触发

在muduo库中通过shutdown去主动关闭当前的连接写操作,需要注意一个思维误区,当shutdown服务器端的写后,只是服务器端不可以再发送数据了(不能在应用层发送数据,即关闭了写缓冲区,但是ack还是可以继续发的),但是需要注意的是服务器端还是可以继续接收数据的,如果客户端继续发送数据,那么服务器端还是可以继续读取的此时触发的是EPOLLIN,但是如果客户端调用close关闭了连接,那么客户端会发送FIN包,则此时的服务器端会触发EPOLLIN+EPOLLDHUP+EPOLLHUP。(如果此时不将该fd从epoll中移除其将一直触发这三个事件)。

我对TCP连接的理解如下:

当客户端通过三次握手和服务器建立连接后,两端之间建立两条连接即C->S和S->C,,,其中C->S负责客户端向服务器发送数据和服务器向客户端发送ack,而S->C负责服务器向客户端发送数据和客户端向服务器发送ack。

    当服务器调用shutdown关闭写时,S->C的连接将会发生关闭。此时服务器不会再向客户端写数据,但是C->S的连接仍然存在,此时客户端还可以向服务器写数据。

    当服务器调用write关闭连接时,S->C的连接会关闭,但是注意虽然此时的C->S还没有关闭但是如果C->S写数据的话,服务器端会向客户端发送RST。

2.2 epoll与RST触发

       如果对端发送了RST给本端,本端的epoll对RST给出的反应是触发EPOLLIN+EPOLLHUP+EPOLLERR

https://blog.csdn.net/halfclear/article/details/78061771?utm_source=blogxgwz8

 

三muduo库中的连接关闭和错误处理方法

    1. muduo库中的连接关闭

muduo库中的连接关闭分为主动关闭和被动关闭以及错误关闭三类(不管怎么关闭都要保证最后将此连接移除),,主动关闭只有shutdown写这一种方法,当用户主动关闭了连接后,服务器将关闭写操作一方,客户端还可以照常向服务器进行写操作,如果客户端一直不调用read来处理shutdown发来的FIN包,那么将一直存在此半连接状态。但是如果一旦read被调用将返回0,从而客户端主动close连接,此时客户端向服务器发送FIN包,服务器的epoll将对此事件触发EPOLLIN+EPOLLHUP事件,服务器的handleRead函数处理此事件返回0,从而将连接移除,最后将此连接彻底移除,

第二种连接的关闭方法是被动关闭,客户端主动发起连接关闭操作,然后epoll触发EPOLLIN事件,调用handleRead函数read返回0,从而关闭移除连接。

第三种是如果连接发生错误导致的连接的关闭。对于epoll而言什么样的事件类型叫错误事件??即EPOLLERR+EPOLLHUP。当epoll触发了这两个中的其中一个都属于连接发生错误(shutdown写后出现的情况不算)。当epoll上被触发了这两种事件后,服务器该采取怎样的操作呢??我在看muduo库源码的时候,我一直很好奇,为什么服务器对于错误的情况都只是调用一个handleError函数将错误信息打印到日志中,但是并没有去关闭和移除此出错的连接。在经过我的测试后,我发现其实EPOLLERR和EPOLLHUP都是和EPOLLIN同时出现的,也就是说这两个错误的类型并不会单独出现。所以由于它们和EPOLLIN一起出现那么在handleRead函数中的read操作会返回0(注意,read如果直接读取RST包会返回-1,但是如果经过epoll接收到RST后触发的EPOLL那么read将返回0),当read返回0后服务器将会调用handleClose函数,从而将连接进行关闭和移除。同时由于handleEvent函数中的if不是采用的if-else模式(为什么不是,我的另外一篇文章中会将)所以handleError操作也会被触发,从而也会将连接出错的相关信息输出到日志当中。(其实EPOLLHUP和EPOLLERR有时候并不是成对出现的,但是它们只要出现一个就意味着连接有错误产生,所以muduo将这两个事件类型放在了一起进行考虑)。另外为了防止出现只有EPOLLHUP而没有EPOLLIN的情况出现(EPOLLERR出现的时候EPOLLHUP也一定会出现所以只考虑EPOLLHUP即可),muduo库还增加了一个if语句。用于处理这种特殊情况,此时muduo直接调用handleClose函数关闭移除连接。

例如如果客户端连接已经关闭,而服务器端仍然用send或者EPOLLOUT向对端发送数据,那么对端将发送RST包,从触发epoll上的EPOLLIN+EPOLLERR+EPOLLHUP,从而开始关闭移除连接和错误处理。

    1. muduo库中的错误处理

对于muduo库而言,有哪些地方需要进行错误处理呢,,常见的就是四种错误,一个是accept错误,这种错误直接写到日志里面就行,不用采取别的措施,一个是read错误,同样对于read返回的错误也是直接写到日志中,对于write的错误需要具体处理(另外一篇文章会详述),对于连接上的错误则需要关闭和移除连接,同时写到日志中。如果只是普通的连接错误那么只用将错误信息写到日志中即可。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值