方案一、通过TCP协议的返回值进行判断
<1>利用select,把socket设置为非阻塞。然后使用select等待该socket的可读事件。如果socket可读,但是recv的返回值是0,则说明socket已经被对端断开,这时候就可以调用close关闭socket。这里还要注意一点,recv还可能返回负数,这个代表socket操作出错。但是仍然应该判断一下errno是否为EINTR。如果errno是EINTR,则说明recv函数是被信号中断返回的,这时候不能判断socket的连接是否正常,也不应该调用close关闭socket。
<2>利用poll的事件。poll本身提供了POLLHUP,POLLERR, POLLNVAL三个事件。如果文件描述符是socket,则POLLHUP代表socket已经断开了连接,在TCP底层就是已经收到了FIN报文。POLLERR表示socket出现了错误,一般情况下是收到了rst报文,或者已经发送了rst报文。这两种情况都应该调用close关闭socket。POLLNVAL代表socket没有打开,这时不能使用close关闭它,而应该根据自己的业务做一些其他的操作。因为关闭一个未打开的socket会出错。
这两种方法都可以很精确地判断tcp连接是否正常,但是仍然有很明显的缺陷。就是它只可以根据TCP操作的返回值来进行判断。如果TCP四次握手没有正常被执行呢?比如连接对端机器直接挂了,那么就不会发送FIN报文给这一端,select不会返回socket可读,poll不会返回socket异常。那么这个死链接将会永远检测不到。直到写这个socket的时候,对端直接返回一个ret报文,这时才知道这个连接已经断掉了。这就意味着tcp连接异常可能永远检测不到,或者检测到的延迟非常大。这对于一些资源宝贵而且要求高性能的服务器是不能接受的,比如游戏服务器,比如搜索服务器。
方案二、在第一种方案的基础上设置socket的 keep alive 机制
方案一的主要缺陷在于检测不及时,或者根本检测不到。TCP协议提供了keep alive机制。如果开启了这个特性(暂时称开启了keep alive的一端为开启端),在默认情况下,开启的着一端的socket相关结构中会维护一个定时器,默认是2小时。如果在2小时内两端没有数据往来,那么开启端就会给另一端发送一个ack空报文。这时候分几种情况:
<1>对端机器可达,而且TCP相关组件运行正常。那么对端就会给开启端发送一个ack空报文。这时开启端就知道对端是正常的,意味着tcp连接也没有问题。开启端会重新初始化定时器,等待下一个超时的到来。需要注意的是,如果两端之间有数据往来,定时器也会被重新初始化为2个小时。
<2>对端挂了,或者正在重启,还没有完全起来。或者对端服务器不可达。 这种状态的对端是不会响应这个ack的。开启端的 keep alive 机制会把这种情况当探测超时来处理,并且重新发送ack到对端。当超时次数超过一定限制,keep alive 就认为这个tcp连接有问题。典型值是每次75秒,超时9次。
<3>对端挂过,但是已经重启完成。这时候发送这个ack和写已经关闭的socket是一种情况,对端会返回一个rst报文,这样开启端就知道tcp连接出问题了。
可以看出 keep alive 机制弥补了方案一种不能判断没有进行正常四次挥手连接出现问题的缺陷。默认的发送超时和发送间隔都是可以调整的。
tcp_keepalive_time: KeepAlive的空闲时长,默认是2小时
tcp_keepalive_intvl: KeepAlive探测包的发送间隔,默认是75s
tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认是9次
这3个参数使用 setsockopt函数都是可以配置的。
方案二看似已经完美了,能够比较精确而且及时地发现有问题的连接。但是还有2个缺点。第一个是 keep alive 机制看似牛逼,但是很多人不建议使用。因为上面说的3个参数很难根据业务场景给出合适的值,设置不好很容易对tcp连接状态发生误判,关闭了一个本来正常的连接。而且没有一个主动通知应用层的方式。比如socket连接出错了,TCP协议接到了rst,fin,或者keep alive判断出socket有问题了,但是并不会主动去通知应用层,必须我们自己 recv socket或者等待错误事件才能得到这个错误。第二个是很多场景下,keep alive 检测仍然不够及时,比如对端挂了,最长需要等待 tcp_keepalive_intvl * tcp_keepalive_probes时间才可以检测出来,而且这两个值还不能设置得太小,太小了容易误判。
方案三、应用层的心跳
这种形式的心跳设计就比较多样化了,而且灵活,可以很好地适应业务场景。唯一的缺点就是要自己写代码。我目前接触到的就是定期进行RPC调用。看RPC调用是否正常,如果返回错误或者抛出异常,就说明连接有问题。
服务器返回给浏览器的数据类型不对。数据返回错误是指向服务器发送请求,服务器返回的数据是错误的,或者是没有数据返回。
目测这种问题无法解决,问题出在服务器一端,等待服务器修复或者把浏览器的版本更新一下,也可以试着浏览器痕迹删除一下看看能否正常运转。
具体意思如下:
1、404 Not Found
请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。
404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。出现这个错误的最有可能的原因是服务器端没有这个页面。
2、500 Internal Server Error
服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。
3、400 Bad Request
(1)语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
(2)请求参数有误。
扩展资料:其他错误信息常见HTTP状态码:
401 Unauthorized
当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息。客户端可以重复提交一个包含恰当的 Authorization 头信息的请求。
如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书。
如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。
402 Payment Required
该状态码是为了将来可能的需求而预留的。
403 Forbidden
服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个 HEAD 请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。
当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。
参考资料:百度百科-HTTP状态码
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)