如何设置TCP sokcet连接的linux tcp超时设置时间

tcp长连接和保活时间
tcp长连接和保活时间
TCP协议中有长连接和短连接之分。短连接在数据包发送完成后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即我们通常所说的Keepalive(存活定时器)功能。 & &
默认的Keepalive超时需要7,200,000 milliseconds,即2小时,探测次数为5次。它的功效和用户自己实现的心跳机制是一样的。开启Keepalive功能需要消耗额外的宽带和流量,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,Keepalive设置不合理时可能会因为短暂的网络波动而断开健康的TCP连接。
keepalive并不是TCP规范的一部分。在Host Requirements RFC罗列有不使用它的三个理由:(1)在短暂的故障期间,它们可能引起一个良好连接(good connection)被释放(dropped),(2)它们消费了不必要的宽带,(3)在以数据包计费的互联网上它们(额外)花费金钱。然而,在许多的实现中提供了存活定时器。
一些服务器应用程序可能代表客户端占用资源,它们需要知道客户端主机是否崩溃。存活定时器可以为这些应用程序提供探测服务。Telnet服务器和Rlogin服务器的许多版本都默认提供存活选项。
个人计算机用户使用TCP/IP协议通过Telnet登录一台主机,这是能够说明需要使用存活定时器的一个常用例子。如果某个用户在使用结束时只是关掉了电源,而没有注销(log off),那么他就留下了一个半打开(half-open)的连接。如果客户端消失,留给了服务器端半打开的连接,并且服务器又在等待客户端的数据,那么等待将永远持续下去。存活特征的目的就是在服务器端检测这种半打开连接。
也可以在客户端设置存活器选项,且没有不允许这样做的理由,但通常设置在服务器。如果连接两端都需要探测对方是否消失,那么就可以在两端同时设置(比如NFS)。
keepalive工作原理:
若在一个给定连接上,两小时之内无任何活动,服务器便向客户端发送一个探测段。(我们将在下面的例子中看到探测段的样子。)客户端主机必须是下列四种状态之一:
1) 客户端主机依旧活跃(up)运行,并且从服务器可到达。从客户端TCP的正常响应,服务器知道对方仍然活跃。服务器的TCP为接下来的两小时复位存活定时器,如果在这两个小时到期之前,连接上发生应用程序的通信,则定时器重新为往下的两小时复位,并且接着交换数据。
2) 客户端已经崩溃,或者已经关闭(down),或者正在重启过程中。在这两种情况下,它的TCP都不会响应。服务器没有收到对其发出探测的响应,并且在75秒之后超时。服务器将总共发送10个这样的探测,每个探测75秒。如果没有收到一个响应,它就认为客户端主机已经关闭并终止连接。
3) 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。
4) 客户端主机活跃运行,但从服务器不可到达。这与状态2类似,因为TCP无法区别它们两个。它所能表明的仅是未收到对其探测的回复。
服务器不必担心客户端主机被关闭然后重启的情况(这里指的是操作员执行的正常关闭,而不是主机的崩溃)。当被操作员关闭时,所有的应用程序进程(也就是客户端进程)都将被终止,客户端TCP会在连接上发送一个FIN。收到这个FIN后,服务器TCP向服务器进程报告一个文件结束,以允许服务器检测这种状态。
在第一种状态下,服务器应用程序不知道存活探测是否发生。凡事都是由TCP层处理的,存活探测对应用程序透明,直到后面2,3,4三种状态发生。在这三种状态下,通过服务器的TCP,返回给服务器应用程序错误信息。(通常服务器向网络发出一个读请求,等待客户端的数据。如果存活特征返回一个错误信息,则将该信息作为读操作的返回值返回给服务器。)在状态2,错误信息类似于&连接超时&。状态3则为&连接被对方复位&。第四种状态看起来像连接超时,或者根据是否收到与该连接相关的ICMP错误信息,而可能返回其它的错误信息。
linux内核包含对keepalive的支持。其中使用了三个参数:tcp_keepalive_time(开启keepalive的闲置时 长)tcp_keepalive_intvl(keepalive探测包的发送间隔)和tcp_keepalive_probes (如果对方不予应答,探测包的发送次数);在liunx中,keepalive是一个开关选项,可以通过函数来使能。具体地说,可以使用以下代码:
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,同时置上errno为ETIMEDOUT。此时TCP的状态是断开的。
keepalive参数设置代码如下:&
// 开启KeepAlive
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
if (nRet == SOCKET_ERROR)
return FALSE;
// 设置KeepAlive参数
tcp_keepalive alive_in & & & & & & & &= {0};
tcp_keepalive alive_out & & & & & & & &= {0};
alive_in.keepalivetime & & & & & & & &= 5000; & & & & & & & &// 开始首次KeepAlive探测前的TCP空闭时间
alive_in.keepaliveinterval & & & &= 1000; & & & & & & & &// 两次KeepAlive探测间的时间间隔
alive_in.onoff & & & & & & & & & & & & & & & &= TRUE;
unsigned long ulBytesReturn = 0;
nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
&alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
if (nRet == SOCKET_ERROR)
return FALSE;
开启Keepalive选项之后,对于使用IOCP模型的服务器端程序来说,一旦检测到连接断开,GetQueuedCompletionStatus函数将立即返回FALSE,使得服务器端能及时清除该连接、释放该连接相关的资源。对于使用select模型的客户端来说,连接断开被探测到时,以recv目的阻塞在socket上的select方法将立即返回SOCKET_ERROR,从而得知连接已失效,客户端程序便有机会及时执行清除工作、提醒用户或重新连接。
TCP连接非正常断开的检测(KeepAlive探测)
此处的&非正常断开&指TCP连接不是以优雅的方式断开,如网线故障等物理链路的原因,还有突然主机断电等原因
有两种方法可以检测:1.TCP连接双方定时发握手消息 2.利用TCP协议栈中的KeepAlive探测
第二种方法简单可靠,只需对TCP连接两个Socket设定KeepAlive探测。
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'本帖子已过去太久远了,不再提供回复功能。使用Tcp链接,检测网络是否真正连通(设置tcp连接超时) - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了8611字,被33人关注,获得了34个喜欢
使用Tcp链接,检测网络是否真正连通(设置tcp连接超时)
demo地址 :参考资料 :
附带asySocket的使用方法 :
序言网络连接状态检测对于我们的 iOS app开发来说是一个非常通用的需求。为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示。对绝大部分iOS开发者来 说,从苹果示例代码改变而来的各种Reachablity框架是实现这个需求的普遍选择,比如这个库。但事实上,基于此方案的所有实现,都无法帮助我们检 测真正的网络连接状态,它们能检测的只是本地连接状态;这种情况包括但不限于如下场景:1.现在很流行的公用wifi,需要网页鉴权,鉴权之前无法上网,但本地连接已经建立;2.存在了本地网络连接,但信号很差,实际无法连接到服务器;3.iOS连接的路由设备本身没有连接外网。CocoaChina上已有很多网友对此进行提问和吐槽,比如:
中有如下说明,告诉我们其能力受限于此:"Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN."而苹果的则告诉了我们更多: "Reachability does not guarantee that the data packet will actually be received by the host. "Reachability相关的框架在底层都是通过SCNetworkReachability来实现网络检测的,所以无法检测实际网络连接情况。
有鉴于此,笔者希望打造一个通用、简单、可靠的实际网络连接状态检测框架,于是demo :CheckNetwork 诞生了
好了废话不多说了,使用方法很简单,看一眼就会了,这里主要说明一下原理!
1.利用底层传输协议tcp连接,去connect一个服务器,如果connect的返回值&0 (小于0代表失败了)则,connect失败,直接返回错误;2.connect的返回值大于等于0,也不要高兴,此时并不百分之一百的说明正真能上网了.因为经过笔者测试,对于一些商超需要输入手机号码获取密码然后认证的网络来说,此时能connect的通,但是打开手机浏览器页面,仍然返回的是port页(就是弹出来恶心的广告页面,让你输入手机号去认证的).3.对于2的解决办法 :connect通之后,需要send或者write一个buff,然后再recvive或者red返回值,检测返回值的大小和值,此时如果都正确的话,则代表的确可以真正联网了.4.对于3的补充:connect连接是阻塞模式,所以检测的时候最好开线程,不然会卡死.反正如果不设置超时时间的话,默认是75秒返回错误(就是不能连接,毕竟要等人家三次握手成功也不是一件容易的事情).5.遇到的问题 : 首先,因为我们项目需求,首先检测一下连接的网络是否真正能上网,虽说在子线程检测,但是这个方法要是卡75秒的话,直接影响到整个认证流程,所以根本就不能等那么久,最多3s钟,所以就有必要给socket设置超时时间,开始的思路是利用函数setsockopt设置超时时间,但是最后发现并没有什么卵用,经过艰苦卓绝的百度,终于找到一种方法,利用connect时的一个返回值 errno和 select函数,就解决了这个问题;值得一提的是,使用select函数, 得先把socket设置成非阻塞模式.Send一个buff之前,再设置成阻塞模式,不然接收值,永远和不设超时时间接收值对上号!(特么的都是坑过来的!)6.对于遇到的问题的补充:对于一般网络很好的情况,上述基本能解决了.但是对于我们公司来说,用一个差评的设备,一个差评的AP,就会有各种难以预料到的错误.比如在Send一个buff的时候,AP太差,服务器收不到,通信信道会出问题,特么的就会蹦,而且蹦的和断点一样,下一步之后会莫名奇妙的好了.有时候又莫名奇妙的闪退掉.所以请看下面的代码
//设置不被SIGPIPE信号中断,物理链路损坏时才不会导致程序直接被Terminate
//在网络异常的时候如果程序收到SIGPIRE是会直接被退出的。
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );
//这个bug折磨死人!
在设置完阻塞模式后,加上这一句代码,程序就不会因为通信信道不好而闪退了!
先设置宏定义,导入头文件
#define HOST_IP @"222.73.136.209"
#define HOST_PORT 6022
#import &arpa/inet.h&
#define SUCCESS 1
#define FAIL -1
//下面奉上全部代码
//直接通过BOOL值判断网络是不是真的连通!
-(BOOL)TryCheckNetCanUse {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd & 0) {
// [self showNetworkStatus:@"未联网"];
return NO;
if ([self socket_set_non_blocking:sockfd]&0) {
// [self showNetworkStatus:@"未联网"];
return NO;
int i = 0;
while (i&5) {
if ([self try_connect:sockfd ip:"222.73.136.209" port:6022 timeout:3] == SUCCESS){
return YES;
return NO;
return YES;
//通过ip和port连接
-(int)try_connect:(int)sockfd ip:(const char *)ip
port:(unsigned short)port timeout:(int) timeout{
struct sockaddr_in server_
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) & 0)
return FAIL;
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) & 0) {
if (errno != EINPROGRESS && errno != EWOULDBLOCK)
return FAIL;
tv.tv_sec =
tv.tv_usec = 0;
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
int n = select(sockfd + 1, NULL, &wset, NULL, &tv);
if (n & 0) {
/* select出错 */
return FAIL;
} else if (n == 0) {
/* 超时 */
return FAIL;
return SUCCESS;
//在connect成功之后,设成阻塞模式
flags = fcntl(sockfd, F_GETFL,0);
flags &= ~ O_NONBLOCK;
fcntl(sockfd,F_SETFL, flags);
/***************************************************/
//设置不被SIGPIPE信号中断,物理链路损坏时才不会导致程序直接被Terminate
//在网络异常的时候如果程序收到SIGPIRE是会直接被退出的。
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );
//----这个bug折磨死我了----!
/***************************************************/
unsigned char bytes[]={83, 83,72, 45,50,46,48,45,79,112,101,110,83,83,72,95,53,46,50,13,10,0};
if (send(sockfd, bytes, strlen ((char *)bytes), 0)&0) {
return FAIL;
char buff[128];
//设置接收时间超时
struct timeval Rectimeout={3,0};//3s
int ret=setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&Rectimeout,sizeof(Rectimeout));
//如果ret==0 则为成功,-1为失败,这时可以查看errno来判断失败原因
if (ret & 0) {
return FAIL;
long retr = recv(sockfd, buff, 128, 0);
if(retr ==-1&&errno==EAGAIN)
printf("Recvive timeout\n");
return FAIL;
NSLog(@"retr is----&%ld buff is ---& %s",retr,buff);
if (retr & 0) {
if (strncmp(buff,"SSH",3)==0) {
NSLog(@" connect is ---&%s",buff);
//[self showNetworkStatus:@"已经联网了"];
close(sockfd);
return SUCCESS;
close(sockfd);
// [self showNetworkStatus:@"未连接网络!"];
return FAIL;
//检测阻塞还是非阻塞
-(int) socket_set_non_blocking:(int)socked{
flags = fcntl(sockfd, F_GETFL, 0);
if (flags == -1) {
return -1;
flags |= O_NONBLOCK;
if (fcntl(sockfd, F_SETFL, flags) == -1) {
return -1;
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:本帖子已过去太久远了,不再提供回复功能。linux socket的connect函数如何设置超时?
要评论问题请先或
- 80后IT男
赞同来自: 、、、
要参与问题请先或
- Where amazing happens!
赞同来自: 、
要参与问题请先或
- What's the fucking is going on?
要参与问题请先或
- 90后程序员
要参与问题请先或
浏览: 10939
关注: 6 人

我要回帖

更多关于 tcp 连接超时 的文章

 

随机推荐