`
cuisuqiang
  • 浏览: 3954198 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
3feb66c0-2fb6-35ff-968a-5f5ec10ada43
Java研发技术指南
浏览量:3665178
社区版块
存档分类
最新评论

如何判断Socket连接失效

阅读更多

现在都搞升级,本人也也使用JDK6进行开发。在开发工程中对Socket进行管理时对于这个连接的超时和是否失效进行研究。结果网上的资料很是让人失望,可以说google和百度下来,前几页原创很少都是抄袭。

说正经的,对于连接超时和失效肯定会想到设置超时时间和判断连接是否可用。但是设置超时时间后起作用是在调用read方法的时候,如果只是设置了超时时间却没有调用read,那么就算服务端中断连接,客户端也是无法得知的。而且就算read异常,当前的连接仍然是有效的。

我们来看如下代码运行后再继续:

服务端:

package com.service;
import java.net.*;
/**
 * @说明 从这里启动一个服务端监听某个端口
 * @author 崔素强
 */
public class DstService {
	public static void main(String[] args) {
		try {			
			// 启动监听端口 8001
			ServerSocket ss = new ServerSocket(8001);
			// 没有连接这个方法就一直堵塞
			Socket s = ss.accept();
			// 将请求指定一个线程去执行
			new Thread(new DstServiceImpl(s)).start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

然后我们来看执行类,执行类在收到连接5秒后中断连接:

 

package com.service;
import java.net.Socket;
/**
 * @说明 服务的具体执行类
 * @author 崔素强
 */
public class DstServiceImpl implements Runnable {
	Socket socket = null;
	public DstServiceImpl(Socket s) {
		this.socket = s;
	}
	public void run() {
		try {
			int index = 1;
			while (true) {
				// 5秒后中断连接
				if (index > 5) {
					socket.close();
					System.out.println("服务端已经将连接关闭!");
					break;
				}
				index++;
				Thread.sleep(1 * 1000);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

我们在写一个客户端进行实验:

package com.client;
import java.net.*;
/**
 * @说明 服务的客户端,会请求连接并实时打印连接对象的一些信息,但是不会进行流的操作
 * @author 崔素强
 */
public class DstClient {
	public static void main(String[] args) {
		try {
			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();
		}
	}
}

  

至于输出结果,虽然服务端已经中断连接,但是客户端一直输出下面内容:

true
false
true
false
false
------------------------

 从连接对象的属性信息来看,连接似乎没有中断。但实际虽然内存对象可用,但是物理连接已经失效。所以和网上其他抄袭来抄袭去的说法一样,靠连接对象属性来判断连接的可用性是不可行的

大家会说那就判断调用read方法是否报错呗。我之前有文章已经讨论了关于调用网络里面流的一些内容,在没有判断这个流可用之前,我们是不会调用read方法的,当然具体你是怎么做的我不知道我在说我的情况!

读取网络数据流时的那个方法是这样的:

public static byte[] inputStreamToByte(InputStream inStream)
		throws Exception {
	int count = 0;
	int haveCheck = 0;
	// 如果在网络传输中数据没有完全传递,则方法返回0
	while (count == 0) {
		count = inStream.available();
		haveCheck++;
		if (haveCheck >= 50)
			return null;
	}
	byte[] b = new byte[count];
	inStream.read(b);
	return b;
}

 就是说我们不会直接调用read方法,而available方法在流没有完整和网络中断时都会返回0,不会报错。

就是说就算你设置超时时间设置保持连接这些东西,只要你没有调用read的机会,你的程序就不会出问题。当然如果程序一直不调用read方法,那这个程序可真的够扯淡的了。

其实只要在使用这个连接的时候判断这个连接的可用性就行了,不要等着什么超时。

判断连接可用虽然网上一大片,其实就是那么回事,手动发送心跳包。

socket.sendUrgentData(0xFF); // 发送心跳包

 乳沟你的连接已经中断,那么这个方法就会报错。

至于什么是心跳包,直接上理论吧。

心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包。 用来判断对方(设备,进程或其它网元)是否正常运行,采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经离线。用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线,也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。 比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。发包方:可以是客户也可以是服务端,看哪边实现方便合理,一般是客户端。服务器也可以定时发心跳下去。一般来说,出于效率的考虑,是由客户端主动向服务器端发包,而不是服务器向客户端发。客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否则,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理!

当然不能单纯理解心跳包就是往对方放松数据,因为心跳包是用于状态验证的,不是真实的数据。

我们来看如下例子,服务端不变:

package com.client;
import java.net.*;
/**
 * @说明 服务的客户端,会请求连接并实时打印连接对象的一些信息,但是不会进行流的操作
 * @author 崔素强
 */
public class DstClient {
	public static void main(String[] args) {
		try {
			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)

 那就是说,只要你的服务端断了,调用方法就会出错!

至于我说的他不会作为可见的数据你可以更改服务端代码打印客户端内容,你会发现服务端不会将心跳包内容展示给你!

InputStream ips = socket.getInputStream();
byte[] bt = inputStreamToByte(ips);
if(null != bt)
	System.out.println(new String(bt));
else
	System.out.println("Bt is null");
System.out.println("****************************");

 bt会一直是Null。为什么?因为我说的是对的!

哥通过示例说问题,也许不对有纰漏,但是咱绝对不去Copy,因为咱已经看厌了Copy!

 

请您到ITEYE看我的原创:http://cuisuqiang.iteye.com

或支持我的个人博客,地址:http://www.javacui.com

 

14
0
分享到:
评论
16 楼 DEMONU 2015-05-14  
虽然发送0xFF不会展示,但是听说会造成发送的数据混乱,楼主遇到过没
15 楼 cuisuqiang 2013-07-15  
forGG 写道
直接用开源框架啊,mima啊什么的里面已经做好心跳了

我现在用的就是mina
14 楼 forGG 2013-07-15  
直接用开源框架啊,mima啊什么的里面已经做好心跳了
13 楼 ikrboy 2012-11-22  
其实让我比较困扰的问题就是,我客户端没有断开socket,而是网络出现了问题,而服务器端检测不出socket断开了,就没有强制关掉socket,让客户端以为还是处于连接状态。socket编程真是一门大学问。
12 楼 ikrboy 2012-11-22  
cuisuqiang 写道
ikrboy 写道
楼主你好,请问可以用在服务器端验证client是否断开的情况吗?谢谢

如果是程序断开的或者是转换器主动断开的话,心跳包完全可以检测到,read方法也会报错连接异常!
但是如果是网线中断的话,目前没有发现有效检测方法,我的做法是如果检测到同一个IP下同一个端口的连接,服务端则标记之前处理这个连接的线程主动退出,然后缓存中记录处理该连接的线程为新的线程!
老线程在一段时间后会报错,然后退出,即使不是因为报错退出,因为已经标记他失效,所以他仍然会退出!
不知道这样解释是否满意,欢迎继续讨论!

解释的很详细,我回去慢慢研究,socket编程我还很差,谢谢您的耐心回答。
11 楼 cuisuqiang 2012-11-20  
ikrboy 写道
楼主你好,请问可以用在服务器端验证client是否断开的情况吗?谢谢

如果是程序断开的或者是转换器主动断开的话,心跳包完全可以检测到,read方法也会报错连接异常!
但是如果是网线中断的话,目前没有发现有效检测方法,我的做法是如果检测到同一个IP下同一个端口的连接,服务端则标记之前处理这个连接的线程主动退出,然后缓存中记录处理该连接的线程为新的线程!
老线程在一段时间后会报错,然后退出,即使不是因为报错退出,因为已经标记他失效,所以他仍然会退出!
不知道这样解释是否满意,欢迎继续讨论!
10 楼 ikrboy 2012-11-19  
楼主你好,请问可以用在服务器端验证client是否断开的情况吗?谢谢
9 楼 cuisuqiang 2012-10-14  
Njaubo 写道
楼主你好 我在客户端用sendUrgentData来测试服务器是否断开。服务器端是一直连接上的,但是客户端在循环测试5次后sendUrgentData就报错了,也就是表示服务器端断开了
这是怎么回事呀

sendUrgentData只是不断发送心跳包来检测是否正常连接,但是不能保证你的连接一直正常!代码中socket.setSoTimeout(10);是设置连接的超时时间,看看是不是超时了!
8 楼 Njaubo 2012-10-14  
楼主你好 我在客户端用sendUrgentData来测试服务器是否断开。服务器端是一直连接上的,但是客户端在循环测试5次后sendUrgentData就报错了,也就是表示服务器端断开了
这是怎么回事呀
7 楼 cuisuqiang 2012-05-23  
chenhua_1984 写道
程序正常运行,网线拔掉,socket.sendUrgentData(0xFF)方法要等待大概3分钟才报错?请教楼主这个有没有办法让它立即报错???????

你好,请你参考http://cuisuqiang.iteye.com/blog/1489661,这是我自己写的一个Socket连接池管理器,希望对你有所帮助!
6 楼 chenhua_1984 2012-05-23  
程序正常运行,网线拔掉,socket.sendUrgentData(0xFF)方法要等待大概3分钟才报错?请教楼主这个有没有办法让它立即报错???????
5 楼 moyan03 2012-05-02  
cuisuqiang 写道
moyan03 写道
LZ讲的挺好的,我的那个发送了心跳了没有报错,但是卡在了读input那了。

就是说一直没有读到数据吧

是的,用的DataInputStream。
4 楼 cuisuqiang 2012-04-27  
moyan03 写道
LZ讲的挺好的,我的那个发送了心跳了没有报错,但是卡在了读input那了。

就是说一直没有读到数据吧
3 楼 moyan03 2012-04-27  
LZ讲的挺好的,我的那个发送了心跳了没有报错,但是卡在了读input那了。
2 楼 cuisuqiang 2012-04-26  
zhangzhikaixinya 写道
感谢分享!讲的很好

大家一起进步,欢迎提出批评
1 楼 zhangzhikaixinya 2012-04-26  
感谢分享!讲的很好

相关推荐

    Socket判断远端网络是否断开,简单例子

    - **网络监听**:使用`ConnectivityManager`监控网络状态变化,但这种方法只能检测到设备的网络连接是否可用,不能判断特定Socket连接的状态。 以下是一个简单的示例代码,展示如何使用心跳机制检测网络断开: ``...

    BIO Socket连接池

    在处理大量并发连接时,为了提高性能和资源利用率,通常会引入连接池的概念,这就是"BIO Socket连接池"的核心所在。 BIO( Blocking Input/Output,阻塞I/O)是Java原生I/O模型的一种,它在处理高并发场景时效率较...

    Socket心跳连接_java

    1. **建立Socket连接**:客户端使用`Socket`类的构造函数,指定服务器的IP地址和端口号,建立到服务器的连接。 2. **心跳包设计**:定义心跳包的格式,例如可以是一个简单的JSON对象,包含时间戳和"ping"或"pong"的...

    如何在C语言中判断socket是否已经断开

    下面来介绍判断非阻塞SOCKET是否已经断开的几种方法: 注意要区分不同操作系统分别进行测试, 包括WINDOWS, LINUX和UNIX会各有不同。 在WINDOWS下比较简单,可以使用FD_CLOSE事件判断SOCKET是否已经断开 view ...

    关于SOCKET中的accept函数的解释

    本文将深入探讨SOCKET中的`accept`函数,该函数是服务器端等待并接受客户端连接的关键步骤。 #### 二、accept函数概述 `accept`函数是服务器端处理客户端连接请求的核心方法之一,其主要作用是从内核中取出已经建立...

    librtmp长时间直播socket连接断开的原因

    如果在一定时间内没有数据交换,服务器可能会认为连接失效并关闭。 3. **服务器负载过高**:当FMS服务器处理过多请求时,可能会因为资源耗尽而断开部分连接。 4. **RTMP会话过期**:某些服务器可能设置了会话持续...

    利用socket实现客户端服务器之间简单通信

    三次握手是为了确保连接的可靠性,防止已失效的连接请求报文突然又传到了服务端,导致服务端误认为新的连接请求。具体过程如下: 1. 第一次握手:客户端发送一个带有SYN(同步序列编号)标志的数据包给服务器,请求...

    数据库连接池BoneCP源码分析报告

    5. 连接的健康检查:定期对池中的连接进行健康检查,发现失效的连接会进行移除和重建,确保提供给应用的都是可用的连接。 6. 分布式环境支持:虽然BoneCP本身不直接支持分布式环境,但可以通过配置中间件如Apache ...

    Socket理论知识.doc

    三次握手确保了连接的可靠性,防止已失效的连接请求报文突然又传到了服务端,而四次挥手的原因是TCP的全双工特性,双方都需要各自独立地关闭连接。 在Socket编程中,理解这些基础知识至关重要,因为它们决定了网络...

    利用Keep-Alive处理Socket网络异常断开的方法

    5. **连接断开**:如果经过多次重试仍然没有收到响应,TCP协议会认为连接已经失效,并主动发送一个RST报文断开连接。 #### 四、实现示例 接下来,我们将展示一段C++代码示例,该示例演示了如何在Windows环境下设置...

    android socket 即时通讯开发

    1. **心跳机制**:为防止网络延迟或断开导致的连接失效,需要定期发送心跳包,检测连接状态。 2. **线程管理**:Socket通信通常涉及多线程,需要妥善处理读写操作,避免阻塞主线程。 3. **数据序列化与反序列化**...

    windows 网络通信 socket编程详解 快速入门

    在处理大量Socket连接时,可能会遇到如`WSAENETDOWN`(网络子系统失效)或`WSA_NOT_ENOUGH_MEMORY`(内存不足)这样的错误。为了处理大量的并发连接,可能需要使用线程池来管理多个线程,每个线程处理一组套接字的...

    android 长连接推送

    为了保持连接活跃,通常需要设置心跳机制,防止网络中断导致的连接失效。 五、心跳机制 心跳机制是长连接中必不可少的部分,用于检测连接是否仍然有效。客户端定期发送心跳包到服务器,服务器在一定时间内未收到...

    计算机网络实验二- Socket通信编程与传输协议分析

    三次握手涉及SYN、SYN+ACK和ACK报文段,确保了连接的可靠性,防止已失效的连接请求报文突然又传到了服务器。四次挥手则用于关闭连接,涉及FIN、ACK、FIN+ACK报文段,确保数据传输完毕且双方都准备好断开连接。实验...

    BGA芯片失效分析.pdf

    测试过程中,确保测试板上的铜片与Socket底部的弹片充分接触是非常关键的步骤。完成以上步骤后,进入AutoLearning确认测试板,确保所有设置无误后,可以进行开短路测试。这通常涉及到使用特定的测试软件,根据测试板...

    C/C++ 学习代码实例 - socket编程代码(包括阻塞式、非阻塞式:select/epoll模式)

    三次握手确保了连接的可靠性,防止已失效的连接请求报文突然又传到了服务端,而四次挥手则用于释放连接。在不同阶段,TCP 连接的状态也会发生变化,例如 SYN_SENT、SYN_RCVD、ESTABLISHED、FIN_WAIT_1、FIN_WAIT_2、...

    TCP socket通信实例(心跳)

    KeepAlive是一种检测TCP连接状态的技术,主要用于避免由于网络故障或其他原因导致的连接失效问题。它通过定期发送小的数据包来检查对端是否仍然处于活动状态。 #### 三、KeepAlive配置参数 Linux系统中,可以通过...

    基于Socket的UDP和TCP编程介绍.

    这种机制确保了两端都准备好进行通信的状态,同时也防止了已失效的连接请求报文突然又传送到了服务器的情况。 ##### 2) 基于UDP客户—服务器程序设计基本框架 UDP是一种无连接的协议,因此它没有类似于TCP的“三次...

    mfc socket 客户端/服务器 实例

    CAsyncSocket类提供了各种错误代码,例如WSAECONNRESET表示连接被远程主机重置,WSAENETDOWN表示网络子系统失效等。在编程过程中,我们需要捕获并适当地处理这些错误。 五、关闭与清理 在完成通信后,记得关闭套接...

    精选_基于Socket编程模拟实现文件的分布式存储_源码打包

    它们通过Socket连接到服务器,将文件数据分割成数据块并发送,或者向服务器请求特定文件的数据块。 3. **文件分片**:为了提高存储效率和容错性,文件通常会被分成多个数据块,每个数据块可能被存储在不同的服务器...

Global site tag (gtag.js) - Google Analytics