socket 检测连接断开怎样检测TCP连接是否失效直

1756人阅读
原文 http://cuisuqiang.iteye.com/blog/1453632
现在都搞升级,本人也也使用JDK6进行开发。在开发工程中对Socket进行管理时对于这个连接的超时和是否失效进行研究。结果网上的资料很是让人失望,可以说google和百度下来,前几页原创很少都是抄袭。
说正经的,对于连接超时和失效肯定会想到设置超时时间和判断连接是否可用。但是设置超时时间后起作用是在调用read方法的时候,如果只是设置了超时时间却没有调用read,那么就算服务端中断连接,客户端也是无法得知的。而且就算read异常,当前的连接仍然是有效的。
我们来看如下代码运行后再继续:
package com.
import java.net.*;
public class DstService {
public static void main(String[] args) {
ServerSocket ss = new ServerSocket(8001);
Socket s = ss.accept();
new Thread(new DstServiceImpl(s)).start();
} catch (Exception e) {
e.printStackTrace();
然后我们来看执行类,执行类在收到连接5秒后中断连接:
package com.
import java.net.S
public class DstServiceImpl implements Runnable {
Socket socket = null;
public DstServiceImpl(Socket s) {
this.socket =
public void run() {
int index = 1;
while (true) {
if (index & 5) {
socket.close();
System.out.println("服务端已经将连接关闭!");
Thread.sleep(1 * 1000);
} catch (Exception e) {
e.printStackTrace();
我们在写一个客户端进行实验:
package com.
import java.net.*;
public class DstClient {
public static void main(String[] args) {
Socket socket = new Socket("127.0.0.1", 8001);
socket.setKeepAlive(true);
socket.setSoTimeout(10);
while (true) {
System.out.println(socket.isBound());
System.out.println(socket.isClosed());
System.out.println(socket.isConnected());
System.out.println(socket.isInputShutdown());
System.out.println(socket.isOutputShutdown());
System.out.println("------------------------");
Thread.sleep(3 * 1000);
} catch (Exception e) {
e.printStackTrace();
至于输出结果,虽然服务端已经中断连接,但是客户端一直输出下面内容:
------------------------
从连接对象的属性信息来看,连接似乎没有中断。但实际虽然内存对象可用,但是物理连接已经失效。所以和网上其他抄袭来抄袭去的说法一样,靠连接对象属性来判断连接的可用性是不可行的。
大家会说那就判断调用read方法是否报错呗。我之前有文章已经讨论了关于调用网络里面流的一些内容,在没有判断这个流可用之前,我们是不会调用read方法的,当然具体你是怎么做的我不知道我在说我的情况!
读取网络数据流时的那个方法是这样的:
public static byte[] inputStreamToByte(InputStream inStream)
throws Exception {
int count = 0;
int haveCheck = 0;
while (count == 0) {
count = inStream.available();
haveCheck++;
if (haveCheck &= 50)
return null;
byte[] b = new byte[count];
inStream.read(b);
就是说我们不会直接调用read方法,而available方法在流没有完整和网络中断时都会返回0,不会报错。
就是说就算你设置超时时间设置保持连接这些东西,只要你没有调用read的机会,你的程序就不会出问题。当然如果程序一直不调用read方法,那这个程序可真的够扯淡的了。
其实只要在使用这个连接的时候判断这个连接的可用性就行了,不要等着什么超时。
判断连接可用虽然网上一大片,其实就是那么回事,手动发送心跳包。
socket.sendUrgentData(0xFF);
如果你的连接已经中断,那么这个方法就会报错。
至于什么是心跳包,直接上理论吧。
心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包。 用来判断对方(设备,进程或其它网元)是否正常运行,采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经离线。用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线,也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。
比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。发包方:可以是客户也可以是服务端,看哪边实现方便合理,一般是客户端。服务器也可以定时发心跳下去。一般来说,出于效率的考虑,是由客户端主动向服务器端发包,而不是服务器向客户端发。客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否则,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理!
当然不能单纯理解心跳包就是往对方放松数据,因为心跳包是用于状态验证的,不是真实的数据。
我们来看如下例子,服务端不变:
package com.
import java.net.*;
public class DstClient {
public static void main(String[] args) {
Socket socket = new Socket("127.0.0.1", 8001);
socket.setKeepAlive(true);
socket.setSoTimeout(10);
while (true) {
socket.sendUrgentData(0xFF);
System.out.println("目前是正常的!");
Thread.sleep(3 * 1000);
} catch (Exception e) {
e.printStackTrace();
看到控制台的输出:
目前是正常的!
目前是正常的!
java.net.SocketException: Invalid argument: send
at java.net.PlainSocketImpl.socketSendUrgentData(Native Method)
at java.net.PlainSocketImpl.sendUrgentData(PlainSocketImpl.java:550)
at java.net.Socket.sendUrgentData(Socket.java:928)
at com.client.DstClient.main(DstClient.java:14)
那就是说,只要你的服务端断了,调用方法就会出错!
至于我说的他不会作为可见的数据你可以更改服务端代码打印客户端内容,你会发现服务端不会将心跳包内容展示给你!Error_404_资源不存在
404. 抱歉! 您访问的资源不存在!
请确认您输入的网址是否正确,如果问题持续存在,请发邮件至contact@cnblogs.com与我们联系。& & & 在写TCP服务的时候经常需要面对的问题就是如何知道一个TCP连接当前是否有效,但这个问题对很多初入门的同学来说是很困惑的,主要原因是当对方关闭连接后,另一方无法有效的知道;对于同步操作来说可以通过设置操作超时来解决,但异步操作则没有这样方便的了,那只能等keepalive的检测完成引发异步回调了。 & & &
& & & 那在编写应用的时候一般通讯什么方式来检测连接的有效性呢?解决方法一般有两种一种是设置TCP的keepalive时间,另一种则是通过Ping,Pong的方式来实现。前者相对比较简单通过socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null)方法设置即可,以下主要但要通过Ping,Pong的方式来实现应用层面的TCP连接有效性检测。通过Ping,Pong来处理有两种方式:服务器主动和被动。
这种方式主要是由服务器发起,然后由客户端响应;服务检测每个连接Pong响应情况,如果连接在一段时间内没有Pong回应则把相应连接关闭并处理相关会话资源。
这种方式由Client发起Ping然后由服务端回应Pong,如果Client是同步操作的话其实服务端是不需要应答Pong包。服务端检测每个连接最近的Ping时间,如果超过一段时间没有Ping的情况把相应连接关闭并处理相关会话资源。
从上面的两种方式来看显然是被动模式更节省服务器资源,如果采用主动的话服务器还必须启用一个定时器对现有在线接进行发送Ping操作;被动模式就完全不需要了,只有接收到客户端Ping回应一个Pong操作。
一般情况会用一个定时器隔一段时间对所有Client检测一次,看对应的Ping时间是否超时,如果是则直接关闭和释放资源。但这样是要对所有连接进行扫描,其实在应用中只有很小部分连接是无效的,如果针对所有在线连接进行一个扫描那的确一个比较花成本的工作。为了解决全扫描的情况,可以采用一种简单的算法LRU,通过LRU算法在检测的时候只要扫冷区数据即可,这样就可以达到只扫描Ping超时的连接。LRU具体处理结构如下:
以下给出相关LRU实现的c#版本代码:
/// &summary&
/// 基于LRU算法的连接检测
/// &/summary&
public class LRUDetect:IDisposable
/// &summary&
/// 构建检测器
/// &/summary&
/// &param name="timeout"&超时时间以毫秒为单位&/param&
public LRUDetect(int timeout)
mTimeout =
mTimer = new System.Threading.Timer(OnDetect, null, mTimeout, mTimeout);
private int mT
private System.Threading.Timer mT
private LinkedList&Node& mLinkedList = new LinkedList&Node&();
/// &summary&
/// 更新连接
/// &/summary&
/// &param name="connection"&连接信息&/param&
public void Update(IConnecton connection)
lock (this)
LinkedListNode&LRUDetect.Node& node = connection.N
if (node != null)
node.Value.LastActiveTime = Environment.TickC
mLinkedList.Remove(node);
mLinkedList.AddFirst(node);
node = mLinkedList.AddFirst(new Node());
node.Value.LastActiveTime = Environment.TickC
node.Value.Connection =
connection.Node =
/// &summary&
/// 删除连接
/// &/summary&
/// &param name="connection"&连接信息&/param&
public void Delete(IConnecton connection)
lock (this)
LinkedListNode&LRUDetect.Node& node = connection.N
if (node != null)
node.Value.Connection =
mLinkedList.Remove(node);
private void OnDetect(object state)
lock (this)
int cutime = Environment.TickC
LinkedListNode&Node& last = mLinkedList.L
while (last !=null && last.Value.Detect(cutime,mTimeout))
last.Value.Connection.TimeOut();
last.Value.Connection =
mLinkedList.RemoveLast();
last = mLinkedList.L
/// &summary&
/// 连接描述接口
/// &/summary&
public interface IConnecton
/// &summary&
/// 获取对应在LRU算法中的节点
/// &/summary&
LinkedListNode&LRUDetect.Node& Node
/// &summary&
/// 超时操作,当LRU算法检测到应该连接超时的时候会调用该方法
/// &/summary&
void TimeOut();
/// &summary&
/// 节点信息
/// &/summary&
public class Node
/// &summary&
/// 最后活动时间
/// &/summary&
public int LastActiveT
/// &summary&
/// 相关连接信息
/// &/summary&
public IConnecton C
/// &summary&
/// 检测是否过期
/// &/summary&
/// &param name="cutime"&&/param&
/// &param name="timeout"&&/param&
/// &returns&&/returns&
public bool Detect(int cutime,int timeout)
return Math.Abs(cutime - LastActiveTime) &
/// &summary&
/// 释放对象
/// &/summary&
public void Dispose()
if (mTimer != null)
mTimer.Dispose();
阅读(...) 评论()查看: 2360|回复: 5
请问MySQL如何查看一个连接用的是socket还是TCP协议
wxy0327 该用户已被删除
请问MySQL如何查看一个连接用的是socket还是TCP协议?
wxy0327 该用户已被删除
自己解决,
论坛徽章:43
兄弟可以分享下&&:-)
localhost走的是socket,show processlist可以看到
论坛徽章:5
mysql 中执行命令 status(root@localhost:mysql.sock) [(none)]&\s
--------------
mysql&&Ver 14.12 Distrib 5.0.77, for redhat-linux-gnu (x86_64) using readline 5.1
Connection id:& & & & & & & & 4722
Current database:& & & &
Current user:& & & & & & & & root@localhost
SSL:& & & & & & & & & & & & Not in use
Current pager:& & & & & & & & stdout
Using outfile:& & & & & & & & ''
Using delimiter:& & & & ;
Server version:& & & & & & & & 5.6.18-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)
Protocol version:& & & & 10
Connection:& & & & & & & & [color=Red]Localhost via UNIX socket[/color]
Server characterset:& & & & latin1
Db& &&&characterset:& & & & latin1
Client characterset:& & & & utf8
Conn.&&characterset:& & & & utf8
UNIX socket:& & & & & & & & /data/3308/mysql.sock
Uptime:& & & & & & & & & & & & 3 days 1 hour 46 min 0 sec
Threads: 25&&Questions: 142469&&Slow queries: 1478&&Opens: 1160&&Flush tables: 1&&Open tables: 172&&Queries per second avg: 0.536
--------------
复制代码
wxy0327 该用户已被删除
status 只能看当前连接的Connection,没法看所有连接的。我也是看show processlist
论坛徽章:5
那就是用show processlist
itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有    
 北京市公安局海淀分局网监中心备案编号:10 广播电视节目制作经营许可证:编号(京)字第1149号博客分类:
做网络编程的人对setSoTimeout方法一定很熟悉,都知道是设置连接的超时时间!
但是我在网上找资料时发现很多人把这个超时时间理解成了链路的超时时间!我看了一下JDK 关于这个方法的说明,其实根本不是链路的超时时间!
setSoTimeout
public void setSoTimeout(int timeout)
throws SocketException启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。
如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。
超时值必须是 & 0 的数。超时值为 0 被解释为无穷大超时值。
timeout - 指定的以毫秒为单位的超时值。
SocketException - 如果底层协议出现错误,例如 TCP 错误。
从以下版本开始:
另请参见:
getSoTimeout()
其实说白了他只是read方法的超时时间,这个方法是堵塞的!
写个小例子验证一下:
服务端,收到一个请求后处理,但是只处理一个请求,处理完毕后结束:
import java.io.ByteArrayOutputS
import java.io.InputS
import java.net.InetSocketA
import java.net.ServerS
import java.net.S
import java.net.SocketA
import java.net.SocketE
import java.net.SocketTimeoutE
import java.text.SimpleDateF
import java.util.A
import java.util.D
public class SocketService {
public static void main(String[] args) {
SocketAddress address = new InetSocketAddress("192.168.9.155", 30001);
// 启动监听端口 8001
ServerSocket ss = new ServerSocket();
ss.bind(address);
// 接收请求
Socket s = ss.accept();
new Thread(new T(s)).start();
} catch (Exception e) {
e.printStackTrace();
class T implements Runnable {
public void run() {
System.out.println(socket.toString());
socket.setKeepAlive(true);
socket.setSoTimeout(5 * 1000);
String _pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(_pattern);
while (true) {
System.out.println("开始:" + format.format(new Date()));
InputStream ips = socket.getInputStream();
ByteArrayOutputStream bops = new ByteArrayOutputStream();
int data = -1;
while((data = ips.read()) != -1){
System.out.println(data);
bops.write(data);
System.out.println(Arrays.toString(bops.toByteArray()));
}catch(SocketTimeoutException e){
e.printStackTrace();
}catch(SocketException e){
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
Thread.sleep(1000);
System.out.println(socket.isBound()); // 是否邦定
System.out.println(socket.isClosed()); // 是否关闭
System.out.println(socket.isConnected()); // 是否连接
System.out.println(socket.isInputShutdown()); // 是否关闭输入流
System.out.println(socket.isOutputShutdown()); // 是否关闭输出流
System.out.println("结束:" + format.format(new Date()));
} catch (Exception e) {
e.printStackTrace();
private Socket socket =
public T(Socket socket) {
this.socket =
public Socket getSocket() {
public void setSocket(Socket socket) {
this.socket =
第一个客户端,连接后一直保持连接对象的存活,但是不发送数据,服务端打印:
import java.net.S
public class Client {
public static void main(String[] args) {
Socket socket = new Socket("192.168.9.155", 30001);
socket.setKeepAlive(true);
while(true && null != socket){
Thread.sleep(10 * 1000);
} catch (Exception e) {
e.printStackTrace();
打印如下,可以看到链路一直是活的,间隔超时时间的间隔就打印一组异常信息:
Socket[addr=/192.168.9.155,port=3017,localport=30001]
开始: 11:15:30
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at socket.T.run(SocketService.java:42)
at java.lang.Thread.run(Unknown Source)
结束: 11:15:36
开始: 11:15:36
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at socket.T.run(SocketService.java:42)
at java.lang.Thread.run(Unknown Source)
结束: 11:15:42
开始: 11:15:42
然后我们编写一个客户端,连接后马上关闭连接,也不发送任何数据:
import java.net.S
public class Client {
public static void main(String[] args) {
Socket socket = new Socket("192.168.9.155", 30001);
socket.setKeepAlive(true);
} catch (Exception e) {
e.printStackTrace();
打印如下:
开始: 11:17:42
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at socket.T.run(SocketService.java:42)
at java.lang.Thread.run(Unknown Source)
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at socket.T.run(SocketService.java:42)
at java.lang.Thread.run(Unknown Source)
结束: 11:17:43
开始: 11:17:43
结束: 11:17:44
异常是不一样的,不一样的还有,如果是超时,则五秒钟循环一次,然后是连接中断,则不在循环马上再报错,因为连接已经挂了!但是打印这个连接还是有效的,这个我也不知道怎么回事!
所以,如果大家理解为超时时间内没有数据连接就自动关闭或失效,那么这个理解就非常有问题了!
请您到ITEYE看我的原创:
或支持我的个人博客,地址:
浏览 150506
直接拔网线需要使用心脏机制,A给B一个验证消息若A能正常收到回执消息,则证明A网络正常。
心跳包我尝试了,没用,网线掉后还能正常发送!但是一段时间后才会心跳异常!
cuisuqiang
浏览: 3088074 次
来自: 北京
浏览量:2872248
貌似少了一个java文件哈 ...
cuisuqiang 写道jlcon 写道chenqidou
这个字段用法是如果相互之类超过多少时间没有数据交互,才抛出的正 ...
页面还是jsp,怎么能叫做Freemarker入门示例呢?小强 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 socket 检测是否连接 的文章

 

随机推荐