【Socket】关于socket长连接的心跳包
TCP的socket本身就是长连接的,那么为什么还要心跳包呢?
在smack里有个30s发送一个空消息的线程,同样关于心跳包(keepalive)
据网络搜索到的资料解释如下
- 内网机器如果不主动向外发起连接,外网机没法直连内网的,这也是内网机安全的原因之一,又因为路由器会把这个关系记录下来,但是过一段时间这个记录可能会丢失 ,所有每一个客户端每隔一定时间就会向服务器发送消息,以保证服务器可以随时找到你,这东西被称为心跳包。
- 理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理,重新连接……当然,这个自然是要由逻辑层根据需求去做了。总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。
- 如果不主动关闭socket的话,系统不会自动关闭的,除非当前进程挂掉了,操作系统把占用的socket回收了才会关闭。为什么需要心跳连接主要是判断当前连接是否是有效的、可被使用的。在实际应用中假设一段时间没有数据传输时候理论上说应该连接是没有问题的,但是网络复杂,中途出现问题也是常见的,网线被掐断了、对方进程挂掉了、频繁丢包等,这时候TCP连接是不可使用的,但是对于应用层并不知道,如果需知道网络情况则要很复杂的超时进行了解,TCP从底层就实现了这样的功能。心跳机制是TCP在一段时间间隔后发送确认连接端是否还存在,如果存在的话就会回传一个包确定网络有效,如果心跳包有问题,则通知上层应用当前网络有问题了。
- 这取决于你的server端的超时配置, 每个socket连接都是长连接,它是一个相当占用系统资源的通信管道, 如果这个长连接什么事也没干硬是要占着资源,则server端可以选择关闭这个连接,以省下资源让更多的用户连接进来。
- 所以,即便客户端的是采用死循环while(true)方式连到服务端,对于特定的客户端和服务端类型来说也需要一定时间间隔的心跳(告诉服务端,我还活着,虽然我没干活也没说话,但别把我关了)
以前开发手机游戏时,索爱有一款手机有强制要求,客户端如果超过三分钟无消息发向网络服务端,则会在客户端自动地强制把socket关断。因为socket长连接相对于手机这样资源少的设备来说是宝贵的资源。 (这个强制是指客户端系统自动关的,不是我们代码close的)
- 在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
- 心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
- 其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。
以下是Smack里发送心跳包的代码:PacketWrite.java
void startKeepAliveProcess() { int keepAliveInterval = SmackConfiguration.getKeepAliveInterval(); if (keepAliveInterval > 0) { KeepAliveTask task = new KeepAliveTask(keepAliveInterval); this.keepAliveThread = new Thread(task); task.setThread(this.keepAliveThread); this.keepAliveThread.setDaemon(true); this.keepAliveThread.setName("Smack Keep Alive (" + this.connection.connectionCounterValue + ")"); this.keepAliveThread.start(); } } private class KeepAliveTask implements Runnable { private int delay; private Thread thread; public KeepAliveTask(int paramInt) { this.delay = paramInt; } protected void setThread(Thread thread) { this.thread = thread; } public void run() { try { Thread.sleep(15000L); } catch (InterruptedException localInterruptedException) { } while ((!(PacketWriter.this.done)) && (PacketWriter.this.keepAliveThread == this.thread)) { synchronized (PacketWriter.this.writer) { if (System.currentTimeMillis() - PacketWriter.this.lastActive >= this.delay) { try { PacketWriter.this.writer.write(" "); PacketWriter.this.writer.flush(); } catch (Exception localException) { } } } try { Thread.sleep(this.delay); } catch (InterruptedException localInterruptedException1) { } } } }
另外记一个CSDN上早些年的问题:
- QQ等程序聊天,双方是否使用SOCKET通信?应该不会都是通过服务器吧,那样服务器负担得多大啊?
- 如果是SOCKET,那么多的好友,一下子得建立多少个啊?还得考虑上线下线隐身等乱七八糟的问题。
- 请高手帮忙指点一下这种复杂程序的结构,谢谢。
我还是很有发言权的呀,我毕业设计就是写的这个(不过当时用的是vc写的,带文件传输的)@hl_ghost
我来说下我的思路吧:
1.如何知道谁在线?
Server维护一个list就ok了(存所有人的ip,名字,在线等)
2.如何让服务器随时能找到你?
前提:内网机器如果不主动向外发起连接,外网机没法直连内网的,这也是内网机安全的原因之一吧,又因为路由器会把这个关系记录下来,但是过一段时间这个记录可能会丢失 ,所有每一个客户端 每隔一定时间就会向服务器发送消息,以保证服务器可以随时找到你,这东西被称为心跳包。
3.如何跨内网直连
Nat打洞(难):
我简单说下原理,有两个客户端A,B ,当然必须有Server啦(他可以随时连接A,B)
当A想连B时,A就回从Server那要B的ip,然后与B建立连接(第一次不能成功的,因为看红字)。
这时A告诉Server,我找不到B,你替我告诉他一声,我想与它连接,服务器就告诉B,你给A下一个请帖(B发请求向A)!
这时A再向B发起连接就可以成功了(以后就不用server帮忙了)。
4.如何保证数据的可靠性(难)
滑动窗口协议,这个一句话两句说不清楚啦,自己google下。
5是否在线。
我的设计是每隔40秒客户端把Server中存自己的信息中的在线改为真,而服务器每过45秒就检查这个在线变量是否为真,真的话把他改成假,如果假的话就说明这个人在45秒没有向Server报到=>他网络出现异常了,掉线了,向其它人发这个人的掉线通知。(这么设计原因在于当用户网断了没有发下线通知,我们也能知道他不在线了)
6文件传输(难)
把文件读到buf里,然后每次发1024b(当收到接收方确认后再发下一个1024b)。
参考文章
http://www.cppblog.com/tx7do/archive/2009/11/09/100513.html
http://bbs.csdn.net/topics/270063434