当前位置:C++技术网 > 资讯 > [C++] 在编写服务器代码时,应该选择时钟还是线程来做心跳包处理

[C++] 在编写服务器代码时,应该选择时钟还是线程来做心跳包处理

更新时间:2016-11-13 14:03:55浏览次数:1+次

    在此之前,我先感谢C++技术网之前回答过的问题,接下来我问的问题是接在 Windows Socket IOCP 完成端口之后的。

    C++技术网之前提供的答案,是看指定的一些书籍。我之后也是下载来看,发现书中讲的是很详细,但是这一看也不知不觉花了四个月的时间,有些内容没看完,也有些也没理解好。

    尽管如此,关于 Windows Socket IOCP,我也只能到处找几个源码加书中的理解也勉强算是可以理解的了。但还有一些疑问在我这里,就好比关联到完成端口的套接字,关联是关联了,但是如何解绑呢?这又是很头疼的问题,最后搜索出来的答案是只要 closesocket() 就好了,IOCP 会自动检查句柄是否有效,如果无效,将会自动解绑。虽然我不知道算不算正确,但总比没有答案的好;接下来还有内存异常的处理;内存池等问题。来来回回也算是遇到了很多问题。

    关于其他的一些问题,之后我再慢慢通过C++技术网一一提问吧。

    话说得有点长了,不好意思,接下来是要问的问题。

    在编写服务器代码时,应该选择时钟还是线程来做心跳包处理呢?

    时钟:

    A-1) 优点就是指定每隔一段时间,就会执行到指定的函数。不管指定函数有没有执行完成,到了一定的时间又会去执行。就好比一个队长每隔段时间叫一名队员去做事,然后队长就不管队员是否把事情做完,就去休息,等待下段时间继续叫一名队员去做事。

    A-2缺点:如果在操作全局对象,会造成访问冲突。就像一名队员正在移除已经烂掉的货物,突然来了一名新的队员来操作这批货物,然后就冲突了。这还不算重要,加个锁可以了,重要的是时钟是占用主线程的,会造成卡机现象,而且还需要一个窗口句柄。

    线程:

    B-1) 说白了就是一个队里面只有一个队员来操作这批货物。

    但是我看到别人说明都是推荐时钟来做心跳包,为什么呢?

    最后感谢C++技术网的回答。


C++技术网解答:

    许多细节在书上没有明确的答案,这都需要自己在实践中寻找答案。Windows平台上最好的就是MSDN了。这是开发文档,所有细节都在这里面。书上只是给了基本的原理流程,并不是开发文档。所以在对一个问题很不清楚的情况,网上也找不到合适的答案,最后一招就是MSDN。或者,最好的就是直接看MSDN了。MSDN基本上是英文的,所以这一点要适应。

    问题多了,考虑的细致而深入,这是一个很好的学习状态,请一直保持。解决了一个问题,越是深入的解决,会带来更多的问题。当然,这些新问题已经不是解决前面的基本问题的问题了,而是可以让你水平得到提升,加深你的认识和理解的问题了。不要期待把所有问题都解决完了。就是技术本身都还有很多问题需要解决,更别说你是使用这个技术。保持一个平常心,乐迎问题。

    回到提问的问题,服务器代码中使用时钟还是线程来做心跳处理?

    那么我们首先需要对心跳的目的有一个清晰的了解。

    所谓的心跳,也就是维持一个连接的活跃,可以及时断开已经不存在的连接,释放资源。心跳的频率是非常频繁的,就和人的心跳一样,时时刻刻都在跳动着。因为心跳只是维持连接的活跃,以免对方认为自己死掉了。

    所以心跳的特点是:频率高、数量多、只起到维持连接活跃性的作用。

    为了不让大量的心跳包造成网络拥塞,我们要尽可能减小心跳包的大小。而且心跳包的产生不能被其他因素干扰,比如死锁。而且心跳包要立即发送。因为心跳包很小,所以有可能被缓存,拼合其他数据报文一起发送,以提高网络传输效率,但是心跳可能会因为这个缓存导致延迟。当然,一般心跳间隔时间不会太短,所以立即发送心跳包这个可以根据实际情况来处理。

    所以,对于提问里说的,根据时钟定时器来做心跳包,可能出现资源被锁定而冲突的问题,这已经说明这个设计不合理了。心跳包的作用要很纯粹,不要牵扯其他的东西,否则就不是简单的心跳了。自然引入了更多的问题,这已经不是心跳包的范围了。

    心跳时间也需要设置一个合适的时间,像问题提到的,前一个没有处理完,后一个心跳就来了,这也说明心跳时间太短,不合理。如果时间设置的合理,前一次的处理卡住了,这说明心跳做的事情不合理。或者代码写的有问题。在一个不合理的环境中,我们就不做讨论了。

    现在说说线程和时钟做心跳的问题。

    时钟计时器做心跳,每隔一段时间触发一次,这是非常自然而然的事情。人的心跳也不会因为你的事情忙,就等你忙完才心跳的。心跳的含义就是需要确保定时触发,而不管你是什么状态,心跳都必须执行。在网络程序里,也就是定期的发送心跳包给对方。至于说发心跳包被阻塞导致后续的心跳也被阻塞了,那不是心跳的问题,而是实现的问题。

    换句话说,如果用线程做,上一个心跳被阻塞了,以后的所有心跳也要全部被阻塞。如果这样的话,还被认为合理的话,心跳还有什么意义。假如说一个时间内被阻塞了,导致连续10个心跳包没有发送出去,或者说本应该产生10个心跳的,却只产生了1个心跳,对方在计数5个心跳时间内仍然收不到心跳,就断开了连接。此时你这边的阻塞完成了,可以发送心跳包了,连接断开了,然后后续的心跳全部发不出去了。那么此时心跳还有什么意义呢?

    而且,线程要实现心跳包,也是需要定时的,要么就是在循环中睡眠指定时间做时间间隔。然而,线程睡眠意味着,线程放弃了被调度的机会,等睡眠时间过了之后,线程才有机会被调度。如果系统运行的线程很多,那么线程被调度的几率就大大降低,此时也就导致间隔时间是不准确的,很不稳定,波动会比较大。这一点也不符合心跳的特点。而时钟计时器是固定时间间隔,虽说计时器消息投入窗口消息队列中的执行优先级低,但是一般不会有太大的波动,相对线程来说,会稳定很多,波动小。因为心跳做的事情很简单,就是简单发一个心跳包出去,不至于会卡死界面,除非你使用同步阻塞方式。卡不卡死,主要看你如何去实现,这个原因不能作为评判心跳实现的理由。

    分析了这么多,应该可以很好的解答这个问题了。