本帖最后由 小篱 于 2015-9-21 11:41 编辑

160548pp3pzuk3rrqyzzzp.jpg
  GameRes游资网授权发布 文 / 郭无心

  游戏服务器常常有心跳包的设计。

  我们的心跳包就是为了防止Socket断开连接,或是TCP的连接断开吗?

  答案是否定的,TCP连接的通道是个虚拟的,连接的维持靠的是两端TCP软件对连接状态的维护。

  TCP 连接自身有维护连接的机制,说白了就是自身有长时间没有数据包情况下的判断连接是否还存在的检测,清除死连接,即使在没有数据来往的时候,TCP也就可以(在启动TCP这个功能的前提下)自动发包检测是否连接正常,这个不需要我们处理。

  服务端设计心跳包的目的:

  探知对端应用是否存活,服务端客户端都可以发心跳包,一般都是客户端发送心跳包,服务端用于判断客户端是否在线,从而对服务端内存缓存数据进行清理(玩家下线等);问题在于,通过TCP四次握手断开的设定,我们也是可以通过Socket的read方法来判断TCP连接是否断开,从而做出相应的清理内存动作,那么为什么我们还需要使用客户端发送心跳包来判断呢?

  第一种判断客户端是否在线策略:

  直接监控TCP传输协议的返回值,通过返回值处理应用层的存活判断

  比如在C++当中

  使用poll的IO复用方法时:

  if(fds.revents POLLERR)

  if(fds.events POLLDHUP)

  通过上述判断可以探知TCP连接的正确性从而在服务器也关闭对应的连接

  close() on the file descriptor will release resources that are still being reserved on behalf of the socket. 此时调用close()函数才会释放相关的资源。

 

 

  1. /*如果客户端关闭连接,则服务器也关闭对应的连接,并将用户总数减1*/
  2. users[fds[i].fd] = users[fds[user_counter].fd];
  3. close(fds[i].fd);
  4. fds[i] = fds[user_counter];
  5. i--;
  6. user_counter--;
  7. printf(a client left\n);

  那么心跳包的意义就在于方便的在服务端管理客户端的在线情况,并且可以防止TCP的死连接问题,避免出现长时间不在线的死链接仍然出现在服务端的管理任务中。

  再举下面一个例子说明下为什么TCP自身的四次握手断开机制不能完全保证应用程序探知连接断开从而避免死连接。

  (1)做一个游戏客户端和服务器端的测试,手动强制关闭客户端进程,然后查看服务器的情况,结果往往是,服务器收到了客户端关闭的事件。其实, 这样会忽略一个问题,没有触发异常中断事件,比如网络中断。

  (2)手动关闭客户端进程,事实上并不能测试出想要的结果,因为进程是在应用层的,所以,这种测试方法不能保证网络驱动层也不发送数据报文给服务器。经过测试会发现,当应用层强制结束进程时,对于TCP连接,驱动层会发送reset数据包!而服务器收到这个数据包就可以正常关闭了!

  (3)那么,如果网络异常甚至直接拔掉网线呢,服务器收不到这个数据包,就会导致死连接存在!

  (4)所以,心跳包是必要的,或者使用TCP协议本身的Keep-alive来设置(但是keep-alive不够好)

  我们不能误解TCP连接如同一条绳子,一方断开了,另外一方必然会知道的。事实上TCP连接,这个“面向连接”的物理连接并不存在,它只是抽象出来的概念,是一个虚拟的连接,对于物理层,对于网线、光纤而言,不存在连接不连接的概念,因为,对它们而言,无非就是一些电流脉冲而已,具体就是一个一个的IP报文。

  TCP的连接,不过是通过ACK、SEQ这些机制来模拟实现的,具体比如差错重传,拥塞控制。

  那么心跳包的检测发送处理对服务器资源的耗费怎么判断?

  这个要看心跳包发送的频率,我们可以自行设置。

  另外这里有个例程,模拟了socket心跳包的C语言实现:

  Socket心跳包异常检测的C语言实现,服务器与客户端代码案例
锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛