TCP是一种面向连接的协议,连接的建立和断开需要通过收发相应的分节来实现。某些时候,由于网络的故障或是一方主机的突然崩溃而另一方无法检测到,以致始终保持着不存在的连接。下面介绍一种方法来检测这种异常断开的情况

1) 在TCP协议中提供了KEEPALIVE检测。该选项使能后,在一个TCP连接上,若指定的一段时间内没有数据交换,则自动发送分节等待对方确认。

SO_KEEPALIVE : 该选项设置是否打开探测

TCP_KEEPIDLE : 开始发送探测分节前等待的空闲时间

TCP_KEEPINTVL: 两次发送探测分节的时间间隔

TCP_KEEPCNT: 判定断开前发送探测分节的次数

2) 设定探测相关选项值

int keepalive = 1;             // 打开探测

int keepidle = 60;        // 开始探测前的空闲等待时间

int keepintvl = 10;        // 发送探测分节的时间间隔

int keepcnt = 3;        // 发送探测分节的次数

3) 设置套接字的属性

if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof (keepalive) < 0)

{

perror(“fail to set SO_KEEPALIVE”);

exit(-1);

}

if (setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof (keepidle) < 0)

{

perror(“fail to set SO_KEEPIDLE”);

exit(-1);

}

if (setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof (keepintvl) < 0)

{

perror(“fail to set SO_KEEPINTVL”);

exit(-1);

}

if (setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof (keepcnt) < 0)

{

perror(“fail to set SO_KEEPALIVE”);

exit(-1);

}

一旦打开KEEPALIVE探测,当TCP连接异常断开后,对sockfd进行recv操作会返回-1,并且errno的值为ETIMEDOUT。

这样一来就可以很方便的在应用程序中检测TCP连接的情况,如果检测到异常断开最简单的处理就是关闭连接。

===================================

WinSock TCP keepalive的机理及使用

TCP 是面向连接的 , 在实际应用中通常都需要检测对端是否还处于连接中。如果已断开连接,主要分为以下几种情况:

1.  连接的对端正常关闭,即使用 closesocket 关闭连接。

2.  连接的对端非正常关闭,包括对端异常关闭,网络断开等情况。

对于第一种情况,很好判断,但是对于第二种情况,可能会要麻烦一些。在网上找到了一些文章,大致有以下两种解决方法:

Ÿ           自己编写心跳包程序

简单的说也就是在自己的程序中加入一条线程,定时向对端发送数据包,查看是否有 ACK ,如果有则连接正常,没有的话则连接断开。

Ÿ           使用 TCP 的 keepalive 机制

这个需要在 WinSock 编程时对当前 SOCKET 进行相应设置即可,比较方便。

为了方便起见,我这里采用 keepalive 机制,下面我就以 WinSock 上我实验得到的结果来大致讲一下其机理和使用方法。

首先说一下 keepalive 来判断异常断开的原理,其实 keepalive 的原理就是 TCP 内嵌的一个心跳包。

以服务器端为例,如果当前 server 端检测到超过一定时间(默认是 7,200,000 milliseconds ,也就是 2 个小时)没有数据传输,那么会 向client 端发送一个 keep-alive packet (该 keep-alive packet 就是 ACK 和当前 TCP 序列号减一的组合),此时 client 端应该为以下三种情况之一:

1. client 端仍然存在,网络连接状况良好。此时 client 端会返回一个 ACK 。 server 端接收到 ACK 后重置计时器,在 2 小时后再发送探测。如果 2 小时内连接上有数据传输,那么在该时间基础上向后推延 2 个小时。

2. 客户端异常关闭,或是网络断开。在这两种情况下, client 端都不会响应。服务器没有收到对其发出探测的响应,并且在一定时间(系统默认为 1000 ms )后重复发送 keep-alive packet ,并且重复发送一定次数( 2000 XP 2003 系统默认为 5 次 , Vista 后的系统默认为 10 次)。

3. 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。(这条摘抄自http://www.cppblog.com/zhangyq/archive/2010/02/28/108615.html ,我自己并不太明白)。

了解了 keep alive 大致的原理,下来看看在程序中怎么用,怎么设置参数:

#include

BOOL bKeepAlive = TRUE;

int nRet = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,(char*)&bKeepAlive, sizeof(bKeepAlive));

if (nRet == SOCKET_ERROR)

{

TRACE(L"setsockopt failed: %d\n", WSAGetLastError());

return FALSE;

}

// set KeepAlive parameter

tcp_keepalive alive_in;

tcp_keepalive alive_out;

alive_in.keepalivetime    = 500;  // 0.5s

alive_in.keepaliveinterval  = 1000; //1s

alive_in.onoff                       = TRUE;

unsigned long ulBytesReturn = 0;

nRet = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),

&alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);

if (nRet == SOCKET_ERROR)

{

TRACE(L"WSAIoctl failed: %d\n", WSAGetLastError());

return FALSE;

}

其中, setsockopt 设置了 keepalive 模式,但是系统对 keepalive 默认的参数可能不符合我们的要求,比如空闲 2 小时后才探测对端是否活跃,所以 WSAIoctl 函数通过 tcp_keepalive 结构体对这些参数进行了相应设置。 tcp_keepalive 这 个 结构体在 mstcpip.h 头文件中有定义:

struct tcp_keepalive {

ULONG onoff ;   // 是否开启 keepalive

ULONG keepalivetime ;  // 多长时间( ms )没有数据就开始 send 心跳包

ULONG keepaliveinterval ; // 每隔多长时间( ms ) send 一个心跳包,

// 发 5 次 (2000 XP 2003 默认 ), 10 次 (Vista 后系统默认 )

};

这个结构体设置了空闲检测时间,及检测时重复发送的间隔时间。详细的可以查询 msdn :http://msdn.microsoft.com/en-us/library/dd877220(VS.85).aspx 。

按照 msdn 上的说法,这些参数也可以通过在注册表里设置,分别为:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveTime

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveInterval

另外,有些人可能已经发现了, tcp_keepalive 这 个结构体中没有对重试次数这个参数的设置,这个参数可以通过注册表来设置,具体位置为:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpMaxDataRetransmissions

关于在注册表中设置这几个参数,我在 XP 和 Server2008 系统中都没有找到, msdn 上说貌似只是支持 server 2003 ,我这里没有实验,具体不太清楚。

设置好 keepalive 以后,我们通过实验来看看当 client 异常退出或是网络断掉的情况下, keepalive 怎么通知我们异常断开的情况。这里采用 select 模式,实验环境为 XP 系统和 Win7 系统,几种情况返回值如下:

1. 正常断开

select 函数正常返回, recv 函数返回 0

2. 异常断开

a)       程序异常退出,如 client 端重启,应用非正常关闭等

select 函数正常返回, recv 函数返回 SOCKET_ERROR , WSAGetLastError () 得到的结果为 WSAECONNRESET(10054) 。

b)      网络断开

结果同上: select 函数正常返回, recv 函数返回 SOCKET_ERROR , WSAGetLastError() 得到的结果为 WSAECONNRESET(10054) 。

P.S. 网上有些文章中写的 WSAGetLastError() 得到的结果为 ETIMEDOUT ,我这里不太清楚为什么和我这里得到的不太一样。

另外,在实验中,我发现了一个和以前理解的不太相同的地方,在这里也记录下来:

对于程序异常退出的情况(这里所说的异常退出包括程序异常关闭、重启等情况,但不包括系统待机休眠),实际上在不开启 keepalive 的情况下也是可以检测到的 ,我这里测试得到在不开启 keepalive 的情况下,异常关闭 client 端程序, server 端 recv 函数会立即返回 SOCKET_ERROR , last error 同样 为 WSAECONNRESET 。但是对于网络断开及系统待机休眠的情况,则必须设置 keepalive 才能检测到,并且对于上述情况,当网络重新连接或者系统恢复后,SOCKET连接并不能恢复。

具体原因我这里也不太清楚,看到有一篇文章是这样写的:“异常关闭下, SOCKET 虚拟通路会被重设,远端正在接受的调用就都会失败”。不知道正确与否,感觉有一定的道理,暂时记录下来。

转自:http://www.rosoo.net/a/201505/17296.html?utm_source=tuicool

java如何处理tcp异常断开_(转)TCP连接异常断开检测相关推荐

  1. tcp ip协议_网络通信-TCP/IP协议族简述

    导读:计算机与网络设备要相互通信需要遵守同样的规则.例如,如何找到通信目标.该使用哪种语言通信.怎么结束通信等规则.不同的硬件.操作系统之间的通信都需要遵循同一种规则,这种规则也称为是协议.下面本文主 ...

  2. 主动断开socket链接_TCP连接与断开详解(socket通信)

    http://blog.csdn.net/Ctrl_qun/article/details/52518479 一.TCP数据报结构以及三次握手 TCP(Transmission Control Pro ...

  3. 主服务器连接异常帮助修复,LOL服务器连接异常怎么解决

    LOL英雄联盟提示"服务器连接异常,即将退出,请重新连接游戏",对此该如何解决呢?下面是学习啦小编收集整理的LOL服务器连接异常怎么解决,希望对大家有帮助~~ LOL服务器连接异常 ...

  4. mysql数据库连接异常,mysql连接数据库时处理连接异常

    连接数据库时处理连接异常 ·· $db = new PDO('mysql:host=localhost;dbname=test', $user, $pass); $rs = $db->query ...

  5. 手机为什么显示服务器异常即将退出游戏,LOL服务器连接异常即将退出怎么回事...

    玩英雄联盟游戏过程中提示"服务器连接异常即将退出",但是检查网络是没有问题的,那么有可能就是防火墙问题了,那么遇到这个问题该怎么办?不知道的朋友赶紧看看以下解决方法吧! Windo ...

  6. 金蝶专业版显示服务器异常,金蝶云专业版服务器连接异常

    金蝶云专业版服务器连接异常 内容精选 换一换 华为云帮助中心,为用户提供产品简介.价格说明.购买指南.用户指南.API参考.最佳实践.常见问题.视频帮助等技术文档,帮助您快速上手使用华为云服务. 路网 ...

  7. java如何处理tcp异常断开_针对TCP连接异常断开的分析

    我们知道,一个基于TCP/IP的客户端-服务器的程序中,正常情况下,我会是启动服务器使其在一个端口上监听请求,等待客户端的连接:通过TCP的三次握手,客户端能够通过socket建立一个到服务器的连接: ...

  8. java 扫描tcp端口号_多线程TCP端口扫描 java实现

    界面部分: import java.awt.Color; import java.awt.Container; import java.awt.FlowLayout; import java.awt. ...

  9. python 监听tcp端口_创建TCP监听_创建TCP监听_功能示例_Python SDK示例_SDK 参考_开发指南_负载均衡 - 阿里云...

    # encoding=utf-8 import json import sys # 调用AcsClient参数进行身份验证 from aliyunsdkcore.client import AcsCl ...

  10. spring 异常捕获异常_使用Spring跟踪异常–第2部分–委托模式

    spring 异常捕获异常 在上一个博客中 ,我开始谈论需要弄清您的应用程序在生产环境中是否行为异常. 我说过,监视应用程序的一种方法是检查其日志文件是否存在异常,如果发现异常,则采取适当的措施. 显 ...

最新文章

  1. python的plt函数_plt.plot画图函数
  2. 计算机端口封闭,如何查询局域网内被封的电脑端口?
  3. 使用Jmeter开发app端接口自动化案例实战
  4. Java – HashMap详细说明
  5. HTML颜色代码表/颜色名(网摘)
  6. api调用实例python_调用阿里云API 的demo示例(java/python)
  7. 以太网的分层架构_以太网矩阵(Ethernet Fabric)简介
  8. python制作软件安装包_Python安装包及开发工具
  9. 整理最全的Java笔试题库之问答题篇-国企笔试题库
  10. 《MMD》制作极乐净土
  11. iptable 帮助
  12. vos对接移动、电信、联通各种线路
  13. 条码打印软件之PDF的拆分合并功能
  14. Q3中国网游业观察:腾讯网易春风得意
  15. android 取消root,彻底告别安卓刷机时代!360超级ROOT正式宣布下线:取消ROOT权限
  16. html markdown插件,MarkDown编辑必备插件
  17. 打包chromium浏览器
  18. Android H5页面调起微信和支付宝付款
  19. 【韩顺平老师MyBatis】
  20. 慕容话币:币圈投资缺点无处遁形,保持良好的心态有多难?

热门文章

  1. 全国职称计算机吧,全国职称计算机考试Excel2003大纲
  2. php在线编辑器fckeditor,[原创]继续给力:PHP中使用FckEditor在线编辑器详解
  3. adb命令 android 串口_android逆向笔记之初学者常用adb命令
  4. shell_exec() php 执行shell脚本
  5. win7计算机找不到脚本文件夹,解决win7开机提示“windows script host 无法找到脚本文件”的方法...
  6. c语言学习-自定义并调用函数求三个数的最小公倍数
  7. lnmp 查看mysql版本_MySQL数据库之Lnmp环境中php-mysql版本问题
  8. 在Kubernetes上部署和管理PostgreSQL
  9. 使用TensorFlow.js进行人脸触摸检测第1部分:将实时网络摄像头数据与深度学习配合使用
  10. NodeJs开发框架fortjs