转载请标明出处:/xx326664162/article/details/48522381 文章出自:薛瑄的博客
你也可以查看我的其他同类文章,也会让你有一定的收货!
setSoTimeout
public void setSoTimeout(int timeout)throws SocketException
启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。如果超过超时值,将引发 .SocketTimeoutException,虽然Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。 参数:
timeout - 指定的以毫秒为单位的超时值。 抛出: SocketException - 如果底层协议出现错误,例如 TCP 错误。
设置超时时间保持连接,只要没有调用read()函数,即使超时程序就不会抛出异常
connect
public void connect(SocketAddress endpoint,int timeout)throws IOException
将此套接字连接到服务器,并指定一个超时值。超时值零被解释为无限超时。在建立连接或者发生错误之前,连接一直处于阻塞状态。
参数: endpoint - SocketAddress timeout - 要使用的超时值(以毫秒为单位)。 抛出: IOException -
如果在连接期间发生错误 SocketTimeoutException 如果在连接之前超时期满 IllegalBlockingModeException 如果此套接字具有关联的通道并且该通道处于非阻塞模式 IllegalArgumentException
如果端点为 null 或者此套接字不支持 SocketAddress 子类
心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包。
用来判断对方(设备,进程或其它网元)是否正常运行,检测TCP的异常断开。
基本原因是服务器端不能有效的判断客户端是否在线,也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。
所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。
发包方:可以是客户也可以是服务端,看哪边实现方便合理,一般是客户端。服务器也可以定时发心跳下去。一般来说,出于效率的考虑,是由客户端主动向服务器端发包,而不是服务器向客户端发。客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否则,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理!当然不能单纯理解心跳包就是往对方放松数据,因为心跳包是用于状态验证的,不是真实的数据。
正常断开连接,该函数会抛出异常
无法判断服务器网络异常断开,如:拔掉网线。
如果使用sendUrgentData()函数,即使在直接拔掉网线的情况下,该函数依然不会抛出异常信息,这个函数只是发送紧急数据,
socket.sendUrgentData(0xFF); // 发送心跳包
服务端:
package com.service; import .*; /** - @说明 从这里启动一个服务端监听某个端口 */ 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(); } } }
服务端执行类:
package com.service; import .Socket; /** * @说明 服务的具体执行类 */ 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 .*; /** * @说明 服务的客户端,会请求连接并实时打印连接对象的一些信息,但是不会进行流的操作*/ 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 ------------------------
在判断服务器是否断开时,首先想到socket类的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但经过试验并查看相关文档,这些方法都是本地端的状态,无法判断远端是否已经断开连接。
测试客户端二:
package com.client; import .*; /** * @服务的客户端,会请求连接并实时打印连接对象的一些信息,但是不会进行流的操作 */ 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(); } } }
这个链接中说到,sendUrgentData(0xFF)和in.readLine(); 这两个方法都只能判断出服务器关闭时断线的情况(正常断开),不能判断出直接拔掉网线的操作。
注意以上程序为单线程,如果断开连接,客户端再进行连接,你会发现,在服务端不能打印出“服务端已经将连接关闭!”详细原因请参加这篇博客
参考:/blog/1453632
/android-142289-1-1.html
关注我的公众号,轻松了解和学习更多技术